├── .editorconfig ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── FILE_TEMPLATE ├── LICENSE ├── README.md ├── RELEASING.md ├── derive ├── Cargo.toml └── src │ ├── attr.rs │ ├── lib.rs │ ├── trait_bounds.rs │ └── utils.rs ├── src ├── build.rs ├── form.rs ├── impls.rs ├── interner.rs ├── lib.rs ├── meta_type.rs ├── portable.rs ├── prelude.rs ├── registry.rs ├── tests.rs ├── ty │ ├── composite.rs │ ├── fields.rs │ ├── mod.rs │ ├── path.rs │ └── variant.rs └── utils.rs └── test_suite ├── Cargo.toml ├── derive_tests_no_std ├── Cargo.toml ├── rust-toolchain └── src │ └── main.rs └── tests ├── codec.rs ├── derive.rs ├── json.rs ├── ui.rs └── ui ├── fail_custom_bounds_missing_skip_type_params.rs ├── fail_custom_bounds_missing_skip_type_params.stderr ├── fail_duplicate_bounds_params.rs ├── fail_duplicate_bounds_params.stderr ├── fail_missing_derive.rs ├── fail_missing_derive.stderr ├── fail_unions.rs ├── fail_unions.stderr ├── fail_with_invalid_capture_docs_attr.rs ├── fail_with_invalid_capture_docs_attr.stderr ├── fail_with_invalid_codec_attrs.rs ├── fail_with_invalid_codec_attrs.stderr ├── fail_with_invalid_scale_info_attrs.rs ├── fail_with_invalid_scale_info_attrs.stderr ├── pass_basic_generic_type.rs ├── pass_combined_attributes.rs ├── pass_complex_generic_self_referential_type.rs ├── pass_custom_bounds.rs ├── pass_custom_bounds_empty.rs ├── pass_custom_bounds_fix_overflow.rs ├── pass_no_implicit_prelude.rs ├── pass_non_static_lifetime.rs ├── pass_raw_identifers.rs ├── pass_self_referential.rs ├── pass_skip_type_params.rs ├── pass_use_codec_attrs_without_deriving_encode.rs ├── pass_with_custom_crate_path.rs └── pass_with_valid_codec_attrs.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: setup 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | profile: minimal 21 | toolchain: stable 22 | override: true 23 | components: rustfmt, clippy 24 | target: wasm32-unknown-unknown 25 | 26 | - name: fmt 27 | run: | 28 | cargo fmt --version 29 | cargo fmt --all -- --check 30 | 31 | - name: clippy 32 | run: | 33 | cargo clippy --version 34 | cargo clippy --all-targets -- -D warnings 35 | 36 | - name: check-all-features 37 | run: | 38 | cargo check --all --all-features 39 | 40 | - name: check-features 41 | run: | 42 | cargo check --no-default-features --features bit-vec 43 | cargo check --no-default-features --features docs 44 | cargo check --no-default-features --features serde 45 | cargo check --no-default-features --features serde,decode 46 | cargo check --no-default-features --features schema 47 | 48 | - name: build 49 | run: | 50 | cargo --version --verbose 51 | cargo build --all 52 | cargo build --all --no-default-features 53 | 54 | - name: test 55 | run: | 56 | cargo test --all --all-features 57 | 58 | # We cannot do usual rust tests in `no_std`. Instead we perform tests in the main function. 59 | # If any assert fails, we get an `Aborted (core dumped)` non-zero exit code. 60 | # This should make the CI fail. 61 | - name: test no-std 62 | run: | 63 | cd ./test_suite/derive_tests_no_std 64 | cargo run --no-default-features 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target/ 2 | **/*.rs.bk 3 | 4 | .DS_Store 5 | 6 | .vscode 7 | .idea 8 | 9 | # Remove Cargo.lock when creating an executable, leave it for libraries 10 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 11 | Cargo.lock 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | - Add `rename` attribute for renaming struct fields [(#209)](https://github.com/paritytech/scale-info/pull/209) 11 | 12 | ## [2.11.6] - 2024-11-20 13 | 14 | - add `#[allow(deprecated)]` attribute to the generated code. 15 | 16 | ## [2.11.5] - 2024-10-24 17 | 18 | - Bump `syn` to 2. 19 | 20 | ## [2.11.4] - 2024-10-22 21 | 22 | - Bump `derive_more` to version 1.0.0. 23 | 24 | ## [2.11.3] - 2024-05-08 25 | 26 | - Bump `proc-macro-crate` to version 3. 27 | 28 | ## [2.11.2] - 2024-04-05 29 | 30 | - Fix a bug in the `PortableRegistry::retain()` method where recursive types (types containing themselves) were not properly handled. 31 | 32 | ## [2.11.1] - 2024-03-22 33 | 34 | - Fix a bug in the `PortableRegistry::retain()` method, where a type's id field was not adjusted to the new position of the type in the retained `Vec`. 35 | 36 | ## [2.11.0] - 2024-03-12 37 | 38 | ### Added 39 | - Implement TypeInfo for `BinaryHeap` [(#200)](https://github.com/paritytech/scale-info/pull/200). 40 | 41 | ## [2.10.0] - 2023-10-20 42 | 43 | ### Added 44 | - Implement `replace_segment` attribute [(#197)](https://github.com/paritytech/scale-info/pull/197) 45 | 46 | ## [2.9.0] - 2023-07-03 47 | 48 | ### Changed 49 | - Expose `PortableType` as public [(#188)](https://github.com/paritytech/scale-info/pull/188) 50 | 51 | ## [2.8.0] - 2023-06-21 52 | 53 | ### Added 54 | - Implement TypeInfo for `Rc` [(#185)](https://github.com/paritytech/scale-info/pull/185) 55 | - Implement TypeInfo for `Duration` [(#184)](https://github.com/paritytech/scale-info/pull/184) 56 | 57 | ## [2.7.0] - 2023-05-15 58 | 59 | ### Added 60 | - `TypeInfo` is now implemented for `Arc` [(#180)](https://github.com/paritytech/scale-info/pull/180) 61 | 62 | ## [2.6.0] - 2023-04-26 63 | 64 | ### Added 65 | - Schema generation [(#178)](https://github.com/paritytech/scale-info/pull/178) 66 | 67 | ## [2.5.0] - 2023-03-29 68 | 69 | ### Added 70 | - ty: Make type fields public [(#176)](https://github.com/paritytech/scale-info/pull/176) 71 | 72 | ## [2.4.0] - 2023-03-23 73 | 74 | ### Added 75 | - portable: Retain the provided type IDs [(#174)](https://github.com/paritytech/scale-info/pull/174) 76 | 77 | ## [2.3.1] - 2022-12-09 78 | 79 | ### Fixed 80 | - Change NonZero* TypeInfo implementation to not be recursive [(#171)](https://github.com/paritytech/scale-info/pull/171) 81 | 82 | ## [2.3.0] - 2022-10-27 83 | 84 | Implement the missing pieces for constructing `PortableRegistry` dynamically at runtime. This allows languages where static rust types are not available to use it. 85 | 86 | ## [2.2.0] - 2022-09-14 87 | 88 | The minimum Rust version is bumped to 1.60.0 in this release owing to using weak dependency crate features. Otherwise there are no breaking changes. 89 | 90 | ### Changed 91 | - Loosen restriction on `TypeDefBitSequence::new()` so that `bitvec` isn't required, and try to avoid pulling in `bitvec` when the `std` feature is enabled [(#168)](https://github.com/paritytech/scale-info/pull/168) 92 | 93 | ## [2.1.2] - 2022-05-18 94 | 95 | ### Fixed 96 | - Strip invisible delimiters from type name [(#156)](https://github.com/paritytech/scale-info/pull/156) 97 | 98 | ## [2.1.1] - 2022-04-11 99 | 100 | ### Fixed 101 | - Restore leading `::` for crate access [(#152)](https://github.com/paritytech/scale-info/pull/152) 102 | 103 | ## [2.1.0] - 2022-04-11 104 | 105 | ### Added 106 | - Add ability to reexport crate [(#145)](https://github.com/paritytech/scale-info/pull/145) 107 | 108 | ### Fixed 109 | - Allow raw identifiers e.g. `r#mod` [(#149)](https://github.com/paritytech/scale-info/pull/149) 110 | 111 | ## [2.0.1] - 2022-02-24 112 | 113 | ### Changed 114 | - Revert bitvec field order to maintain binary compatiblilty 115 | 116 | ## [2.0.0] - 2022-02-07 117 | 118 | ### Changed 119 | - Upgraded to parity-scale-codec 3.0 120 | - Upgraded to bitvec 1.0 121 | - Minimum Rust version is 1.56.1 for edition 2021 122 | 123 | ## [1.0.0] - 2021-09-01 124 | ### Changed 125 | - Replace Range variant with built-in composite definitions [(#130)](https://github.com/paritytech/scale-info/pull/130) 126 | 127 | ## [0.12.0] - 2021-08-25 128 | ### Changed 129 | - Add range getters, combine start and end types [(#126)](https://github.com/paritytech/scale-info/pull/126) 130 | 131 | ## [0.11.0] - 2021-08-25 132 | ### Added 133 | - Add type parameter getters [(#122)](https://github.com/paritytech/scale-info/pull/122) 134 | - Add support for Range and RangeInclusive [(#124)](https://github.com/paritytech/scale-info/pull/124) 135 | - Explicit codec indices for `TypeDef` and `TypeDefPrimitive` enums [(#127)](https://github.com/paritytech/scale-info/pull/127) 136 | 137 | ## [0.10.0] - 2021-07-29 138 | ### Added 139 | - Add capture_docs attribute [(#118)](https://github.com/paritytech/scale-info/pull/118) 140 | 141 | ### Fixed 142 | - Allow codec attributes, in case missing Encode/Decode derives [(#117)](https://github.com/paritytech/scale-info/pull/117) 143 | 144 | ### Changed 145 | - Erase PhantomData fields [(#111](https://github.com/paritytech/scale-info/pull/111), [#115)](https://github.com/paritytech/scale-info/pull/115) 146 | - Make variant index explicit, remove discriminant [(#112)](https://github.com/paritytech/scale-info/pull/112) 147 | - Include type id in serialized type registry [(#114)](https://github.com/paritytech/scale-info/pull/114) 148 | - Improve docs feature [(#116)](https://github.com/paritytech/scale-info/pull/116) 149 | 150 | ## [0.9.2] - 2021-07-09 151 | ### Added 152 | - Add index getter to Variant [(#110)](https://github.com/paritytech/scale-info/pull/110) 153 | 154 | ## [0.9.1] - 2021-07-06 155 | ### Fixed 156 | - Option constructor macro hygiene [(#108)](https://github.com/paritytech/scale-info/pull/108) 157 | 158 | ## [0.9.0] - 2021-06-30 159 | ### Changed 160 | - Reverted parity-scale-codec prerelease requirement from [0.8.0-rc.1] 161 | - Reexport parity-scale-codec for derive [(#106)](https://github.com/paritytech/scale-info/pull/106) 162 | 163 | ### Added 164 | - Add `skip_type_params` attribute [(#96)](https://github.com/paritytech/scale-info/pull/96) 165 | 166 | ## [0.8.0-rc.1] - 2021-06-29 167 | ### Changed 168 | - Bump parity-scale-codec to 2.2.0-rc.2 [(#102)](https://github.com/paritytech/scale-info/pull/102) 169 | 170 | ## [0.7.0] - 2021-06-29 171 | ### Added 172 | - Handle more SCALE attributes: skip, index [(#44)](https://github.com/paritytech/scale-info/pull/44) 173 | - Implement `TypeInfo` for `BTreeSet` [(#85)](https://github.com/paritytech/scale-info/pull/85) 174 | - Implement `TypeInfo` for `Cow` [(#84)](https://github.com/paritytech/scale-info/pull/84) 175 | - Implement `TypeInfo` for up to 20 element tuples [(#92)](https://github.com/paritytech/scale-info/pull/92) 176 | - Add `StaticTypeInfo` convenience trait [(#91)](https://github.com/paritytech/scale-info/pull/91) 177 | - Capture doc comments, add variant and field builders [(#87)](https://github.com/paritytech/scale-info/pull/87) 178 | - Handle `#[codec(index = …)]` in regular enums [(#80)](https://github.com/paritytech/scale-info/pull/80) 179 | - Add new top-level attribute `scale_info(bounds(T: SomeTrait + OtherTrait))` [(#88)](https://github.com/paritytech/scale-info/pull/88) 180 | - (aj-vecdeque) Implement TypeInfo for VecDeque [(#99)](https://github.com/paritytech/scale-info/pull/99) 181 | - Add BitVec support [(#98)](https://github.com/paritytech/scale-info/pull/98) 182 | - Add `docs` feature [(#101)](https://github.com/paritytech/scale-info/pull/101) 183 | 184 | ### Changed 185 | - Upgrade proc-macro-crate to v1 [(#77)](https://github.com/paritytech/scale-info/pull/77) 186 | - Use const generics for array TypeInfo impls [(#54)](https://github.com/paritytech/scale-info/pull/54) 187 | - Replace NonZeroU32 type lookup ids with u32 [(#90)](https://github.com/paritytech/scale-info/pull/90) 188 | - Remove HasCompact::Type bounds [(#83)](https://github.com/paritytech/scale-info/pull/83) 189 | - Unify sequence types [(#100)](https://github.com/paritytech/scale-info/pull/100) 190 | 191 | ### Fixed 192 | - Fix serde and decode features without default features [(#74)](https://github.com/paritytech/scale-info/pull/74) 193 | - Remove type parameter defaults [(#71)](https://github.com/paritytech/scale-info/pull/71) 194 | - Fix trait bounds for associated types [(#76)](https://github.com/paritytech/scale-info/pull/76) 195 | 196 | ## [0.6.0] - 2021-02-05 197 | ### Added 198 | - Add a TypeDef to handle Compact types [(#53)](https://github.com/paritytech/scale-info/pull/53) 199 | - Add feature for enabling decoding [(#59)](https://github.com/paritytech/scale-info/pull/59) 200 | 201 | ### Fixed 202 | - Derive: use known crate name aliases [(#61)](https://github.com/paritytech/scale-info/pull/61) 203 | 204 | ## [0.5.0] - 2021-01-27 205 | ### Added 206 | - Add a new TypeDef variant to handle PhantomData - [(#48)](https://github.com/paritytech/scale-info/pull/48) 207 | - TypeInfo for up to 16 tuples, Clone PortableRegistry - [(#50)](https://github.com/paritytech/scale-info/pull/50) 208 | - Enumerate RegistryReadOnly types, Display Path - [(#27)](https://github.com/paritytech/scale-info/pull/27) 209 | - Add missing 256 bits types which are needed by Solang - [(#25)](https://github.com/paritytech/scale-info/pull/25) 210 | 211 | ### Changed 212 | - Ensure only static lifetimes appear in derived types - [(#39)](https://github.com/paritytech/scale-info/pull/39) 213 | - Remove unused function `MetaType::of()` - [(#49)](https://github.com/paritytech/scale-info/pull/49) 214 | - Use PortableRegistry for encoding and serializing - [(#40)](https://github.com/paritytech/scale-info/pull/40) 215 | - Rename Compact to Portable - [(#41)](https://github.com/paritytech/scale-info/pull/41) 216 | - Parameterize CompactForm String for optional SCALE impl - [(#35)](https://github.com/paritytech/scale-info/pull/35) 217 | - Derive TypeInfo for fields with associated types without bounds - [(#20)](https://github.com/paritytech/scale-info/pull/20) 218 | - Optional serde feature - [(#34)](https://github.com/paritytech/scale-info/pull/34) 219 | - Consolidate common prelude for std and no_std and usage - [(#33)](https://github.com/paritytech/scale-info/pull/33) 220 | - Add informational field type name - [(#30)](https://github.com/paritytech/scale-info/pull/30) 221 | - Unify transparent wrapper types e.g. references - [(#26)](https://github.com/paritytech/scale-info/pull/26) 222 | - Bump `parity-scale-codec` from 1.0 to 2.0 [(#55)](https://github.com/paritytech/scale-info/pull/55) 223 | 224 | ## Fixed 225 | - Fix type name scrubbing to handle nested tuples - [(#47)](https://github.com/paritytech/scale-info/pull/47) 226 | 227 | ## [0.4.1] - 2020-10-11 228 | ### Fixed 229 | - Add missing `extern crate proc_macro;` [(#22)](https://github.com/paritytech/scale-info/pull/24) 230 | 231 | ## [0.4.0] - 2020-10-05 232 | ### Added 233 | - Add public getters for fields in meta type hierarchy [(#22)](https://github.com/paritytech/scale-info/pull/22) 234 | - Implement SCALE encoding and serde deserialization [(#19)](https://github.com/paritytech/scale-info/pull/19) 235 | 236 | ## [0.3.0] - 2020-07-03 237 | ### Changed 238 | - Remove string table, inline strings [(#17)](https://github.com/paritytech/scale-info/pull/17) 239 | 240 | ## [0.2.0] - 2020-06-17 241 | ### Changed 242 | - Remove Metadata supertrait [(#15)](https://github.com/paritytech/scale-info/pull/15) 243 | - Unflatten JSON for type def field [(#14)](https://github.com/paritytech/scale-info/pull/14) 244 | - Improve intra doc links 245 | 246 | ## [0.1.0] - 2020-06-12 247 | ### Added 248 | - First release 249 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scale-info" 3 | description = "Info about SCALE encodable Rust types" 4 | include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | readme.workspace = true 12 | repository.workspace = true 13 | documentation.workspace = true 14 | homepage.workspace = true 15 | categories.workspace = true 16 | 17 | [dependencies] 18 | bitvec = { version = "1", default-features = false, features = ["alloc"], optional = true } 19 | cfg-if = "1.0" 20 | scale-info-derive = { version = "2.11.6", path = "derive", default-features = false, optional = true } 21 | serde = { version = "1", default-features = false, optional = true, features = ["derive", "alloc"] } 22 | derive_more = { version = "1.0.0", default-features = false, features = ["from"] } 23 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 24 | schemars = { version = "0.8", optional = true } 25 | 26 | [dev-dependencies] 27 | scale-info-derive = { version = "2.11.6", path = "derive" } 28 | 29 | [features] 30 | default = ["std"] 31 | std = [ 32 | "bitvec?/std", 33 | "scale/std", 34 | ] 35 | derive = [ 36 | "scale-info-derive" 37 | ] 38 | # Include rustdoc strings in the type metadata. 39 | docs = [ 40 | "scale-info-derive/docs" 41 | ] 42 | # Enables decoding and deserialization of portable scale-info type metadata. 43 | decode = [ 44 | "scale/full" 45 | ] 46 | # Enables type information for bitvec types, matching the name of the parity-scale-codec feature. 47 | bit-vec = [ 48 | "bitvec" 49 | ] 50 | # Enables JSON Schema generation. 51 | schema = [ 52 | "std", 53 | "schemars" 54 | ] 55 | 56 | [workspace] 57 | members = [ 58 | "derive", 59 | "test_suite", 60 | ] 61 | 62 | [workspace.package] 63 | version = "2.11.6" 64 | authors = [ 65 | "Parity Technologies ", 66 | "Centrality Developers ", 67 | ] 68 | edition = "2021" 69 | rust-version = "1.60.0" 70 | license = "Apache-2.0" 71 | readme = "README.md" 72 | repository = "https://github.com/paritytech/scale-info" 73 | documentation = "https://docs.rs/scale-info" 74 | homepage = "https://www.parity.io/" 75 | categories = ["no-std", "encoding"] 76 | -------------------------------------------------------------------------------- /FILE_TEMPLATE: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /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-info · [![build][a1]][a2] [![Latest Version][b1]][b2] 2 | 3 | [a1]: https://github.com/paritytech/scale-info/workflows/Rust/badge.svg 4 | [a2]: https://github.com/paritytech/scale-info/actions?query=workflow%3ARust+branch%3Amaster 5 | [b1]: https://img.shields.io/crates/v/scale-info.svg 6 | [b2]: https://crates.io/crates/scale-info 7 | 8 | A library to describe Rust types, geared towards providing info about the structure of [SCALE 9 | ](https://github.com/paritytech/parity-scale-codec) encodable types. 10 | 11 | The definitions provide third party tools (e.g. a UI client) with information about how they 12 | are able to decode types agnostic of language. 13 | 14 | At its core is the `TypeInfo` trait: 15 | 16 | ```rust 17 | pub trait TypeInfo { 18 | type Identity: ?Sized + 'static; 19 | fn type_info() -> Type; 20 | } 21 | ``` 22 | 23 | Types implementing this trait build up and return a `Type` struct: 24 | 25 | ```rust 26 | pub struct Type { 27 | /// The unique path to the type. Can be empty for built-in types 28 | path: Path, 29 | /// The generic type parameters of the type in use. Empty for non generic types 30 | type_params: Vec, 31 | /// The actual type definition 32 | type_def: TypeDef, 33 | } 34 | ``` 35 | Types are defined as one of the following variants: 36 | ```rust 37 | pub enum TypeDef { 38 | /// A composite type (e.g. a struct or a tuple) 39 | Composite(TypeDefComposite), 40 | /// A variant type (e.g. an enum) 41 | Variant(TypeDefVariant), 42 | /// A sequence type with runtime known length. 43 | Sequence(TypeDefSequence), 44 | /// An array type with compile-time known length. 45 | Array(TypeDefArray), 46 | /// A tuple type. 47 | Tuple(TypeDefTuple), 48 | /// A Rust primitive type. 49 | Primitive(TypeDefPrimitive), 50 | } 51 | ``` 52 | 53 | ## Built-in Type Definitions 54 | 55 | The following "built-in" types have predefined `TypeInfo` definitions: 56 | 57 | - **Primitives:** `bool`, `char`, `str`, `u8`, `u16`, `u32`, `u64`, `u128`, `i8`, `i16`, `i32`, `i64`, `i128`. 58 | 59 | - **Sequence:** Variable size sequence of elements of `T`, where `T` implements `TypeInfo`. e.g. `[T]`, `&[T]`, `&mut 60 | [T]`, `Vec` 61 | 62 | - **Array:** Fixed size `[T: $n]` for any `T` which implements `TypeInfo`, where `$n` is one of the 63 | predefined sizes. 64 | 65 | - **Tuple:** Tuples consisting of up to 10 fields with types implementing `TypeInfo`. 66 | 67 | ## User-defined Types 68 | 69 | There are two kinds of user-defined types: `Composite` and `Variant`. 70 | 71 | Both make use of the `Path` and `Field` types in their definition: 72 | 73 | ### Fields 74 | 75 | A fundamental building block to represent user defined types is the `Field` struct which defines the `Type` of a 76 | field together with its optional name. Builders for the user defined types enforce the invariant that either all 77 | fields have a name (e.g. structs) or all fields are unnamed (e.g. tuples). 78 | 79 | ### Path 80 | 81 | The path of a type is a unique sequence of identifiers. Rust types typically construct a path from 82 | the namespace and the identifier e.g. `foo::bar::Baz` is converted to the path `["foo", "bar", 83 | "Baz"]`. 84 | 85 | ### Composite 86 | 87 | [Composite data types](https://en.wikipedia.org/wiki/Composite_data_type) are composed of a set of `Fields`. 88 | 89 | **Structs** are represented by a set of *named* fields, enforced during construction: 90 | 91 | ```rust 92 | struct Foo { 93 | bar: T, 94 | data: u64, 95 | } 96 | 97 | impl TypeInfo for Foo 98 | where 99 | T: TypeInfo + 'static, 100 | { 101 | type Identity = Self; 102 | 103 | fn type_info() -> Type { 104 | Type::builder() 105 | .path(Path::new("Foo", module_path!())) 106 | .type_params(vec![MetaType::new::()]) 107 | .composite(Fields::named() 108 | .field(|f| f.ty::().name("bar").type_name("T")) 109 | .field(|f| f.ty::().name("data").type_name("u64")) 110 | ) 111 | } 112 | } 113 | ``` 114 | 115 | **Tuples** are represented by a set of *unnamed* fields, enforced during construction: 116 | 117 | ```rust 118 | struct Foo(u32, bool); 119 | 120 | impl TypeInfo for Foo { 121 | type Identity = Self; 122 | 123 | fn type_info() -> Type { 124 | Type::builder() 125 | .path(Path::new("Foo", module_path!())) 126 | .composite(Fields::unnamed() 127 | .field(|f| f.ty::().type_name("u32")) 128 | .field(|f| f.ty::().type_name("bool")) 129 | ) 130 | } 131 | } 132 | ``` 133 | 134 | ### Variant 135 | 136 | [Variant types](https://en.wikipedia.org/wiki/Tagged_union) aka enums or tagged unions are 137 | composed of a set of variants. Variants can have unnamed fields, named fields or no fields at all: 138 | 139 | ```rust 140 | enum Foo{ 141 | A(T), 142 | B { f: u32 }, 143 | C, 144 | } 145 | 146 | impl TypeInfo for Foo 147 | where 148 | T: TypeInfo + 'static, 149 | { 150 | type Identity = Self; 151 | 152 | fn type_info() -> Type { 153 | Type::builder() 154 | .path(Path::new("Foo", module_path!())) 155 | .type_params(vec![MetaType::new::()]) 156 | .variant( 157 | Variants::new() 158 | .variant("A", |v| v.fields(Fields::unnamed().field(|f| f.ty::()))) 159 | .variant("B", |v| v.fields(Fields::named().field(|f| f.ty::().name("f").type_name("u32")))) 160 | .variant_unit("C") 161 | ) 162 | } 163 | } 164 | ``` 165 | 166 | If no variants contain fields then the discriminant can be set explicitly, enforced by the 167 | builder during construction: 168 | 169 | ```rust 170 | enum Foo { 171 | A, 172 | B, 173 | C = 33, 174 | } 175 | 176 | impl TypeInfo for Foo { 177 | type Identity = Self; 178 | 179 | fn type_info() -> Type { 180 | Type::builder() 181 | .path(Path::new("Foo", module_path!())) 182 | .variant( 183 | Variants::new() 184 | .variant("A", |v| v.index(1)) 185 | .variant("B", |v| v.index(2)) 186 | .variant("C", |v| v.index(33)) 187 | ) 188 | } 189 | } 190 | ``` 191 | 192 | ## The Registry 193 | 194 | Information about types is provided within the so-called type registry (`Registry`). 195 | Type definitions are registered there and are associated with unique IDs that the outside 196 | can refer to, providing a lightweight way to decrease overhead instead of using type 197 | identifiers. 198 | 199 | All concrete `TypeInfo` structures have two forms: 200 | 201 | - One meta form (`MetaType`) that acts as a bridge to other forms 202 | - A portable form suitable for serialization. 203 | 204 | The `IntoPortable` trait must also be implemented in order prepare a type 205 | definition for serialization using an instance of the type registry. 206 | 207 | After transformation all type definitions are stored in the type registry. 208 | Note that the type registry should be serialized as part of the metadata structure where the 209 | registered types are utilized to allow consumers to resolve the types. 210 | 211 | ## Encoding 212 | 213 | The type registry can be encoded as: 214 | 215 | - JSON (with the "serde" feature enabled). 216 | - SCALE itself (using `parity-scale-codec`). 217 | 218 | ## Features 219 | 220 | The following optional `cargo` features are available: 221 | 222 | - **serde** includes support for json serialization/deserialization of the type registry. See example [here](https://github.com/paritytech/scale-info/blob/master/test_suite/tests/json.rs). 223 | - **derive** reexports the [`scale-info-derive`](https://crates.io/crates/scale-info-derive) crate. 224 | 225 | ## Known issues 226 | 227 | When deriving `TypeInfo` for a type with generic compact fields e.g. 228 | 229 | ```rust 230 | #[derive(Encode, TypeInfo)] 231 | struct Foo { #[codec(compact)] a: S } 232 | ``` 233 | 234 | You may experience the following error when using this generic type without the correct bounds: 235 | 236 | ```sh 237 | error[E0275]: overflow evaluating the requirement `_::_parity_scale_codec::Compact<_>: Decode` 238 | ``` 239 | 240 | See https://github.com/paritytech/scale-info/issues/65 for more information. 241 | 242 | ## Resources 243 | 244 | - See usage for describing types for [`ink!`](https://github.com/paritytech/ink/blob/master/crates/metadata/src/specs.rs) smart contracts metadata. 245 | - [Original design draft (*outdated*)](https://hackmd.io/0wWm0ueBSF26m2pBG5NaeQ?view) 246 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Release Checklist 2 | 3 | These steps assume that you've checked out the `scale-info` repository and are in the root directory of it. 4 | 5 | We also assume that ongoing work done is being merged directly to the `master` branch. 6 | 7 | 1. Ensure that everything you'd like to see released is on the `master` branch. 8 | 9 | 2. Create a release branch off `master`, for example `release-v0.17.0`. Decide how far the version needs to be bumped based 10 | on the changes to date. 11 | 12 | 3. Check that you're happy with the current documentation. 13 | 14 | ``` 15 | cargo doc --open 16 | ``` 17 | 18 | If there are minor issues with the documentation, they can be fixed in the release branch. 19 | 20 | 4. Bump the crate versions in `./Cargo.toml` and `./derive/Cargo.toml` to whatever was decided in step 2 (basically a find and 21 | replace from old version to new version in this file should do the trick). 22 | 23 | 5. Update `CHANGELOG.md` to reflect the difference between this release and the last. See the `CHANGELOG.md` file for 24 | details of the format it follows. 25 | 26 | First, if there have been any significant changes, add a description of those changes to the top of the 27 | changelog entry for this release. 28 | 29 | 6. Commit any of the above changes to the release branch and open a PR in GitHub with a base of `master`. 30 | 31 | 7. Once the branch has been reviewed and passes CI, merge it. 32 | 33 | 8. Now, we're ready to publish the release to crates.io. 34 | 35 | 1. Checkout `master`, ensuring we're looking at that latest merge (`git pull`). 36 | 37 | ``` 38 | git checkout master && git pull 39 | ``` 40 | 41 | 2. Perform a final sanity check that everything looks ok. 42 | 43 | ``` 44 | cargo test --all-targets 45 | ``` 46 | 47 | 3. Run the following command to publish both crates. 48 | 49 | ``` 50 | (cd derive && cargo publish) && cargo publish 51 | ``` 52 | 53 | 9. If the release was successful, tag the commit that we released in the `master` branch with the 54 | version that we just released, for example: 55 | 56 | ``` 57 | git tag -s v2.7.0 # use the version number you've just published to crates.io, not this one 58 | git push --tags 59 | ``` 60 | 61 | Once this is pushed, go along to [the releases page on GitHub](https://github.com/paritytech/scale-info/releases) 62 | and draft a new release which points to the tag you just pushed to `master` above. Copy the changelog comments 63 | for the current release into the release description. 64 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scale-info-derive" 3 | description = "Derive type info for SCALE encodable types" 4 | 5 | version.workspace = true 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | license.workspace = true 10 | readme.workspace = true 11 | repository.workspace = true 12 | documentation.workspace = true 13 | homepage.workspace = true 14 | categories.workspace = true 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | quote = "1.0" 21 | syn = { version = "2.0", features = ["derive", "visit", "visit-mut", "extra-traits"] } 22 | proc-macro2 = "1.0" 23 | proc-macro-crate = "3" 24 | 25 | [features] 26 | default = ["docs"] 27 | # Include code docs in type metadata. 28 | docs = [] 29 | -------------------------------------------------------------------------------- /derive/src/attr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use syn::{ 16 | parse::{Parse, ParseBuffer}, 17 | punctuated::Punctuated, 18 | spanned::Spanned, 19 | LitStr, Token, 20 | }; 21 | 22 | const SCALE_INFO: &str = "scale_info"; 23 | 24 | mod keywords { 25 | syn::custom_keyword!(scale_info); 26 | syn::custom_keyword!(bounds); 27 | syn::custom_keyword!(skip_type_params); 28 | syn::custom_keyword!(capture_docs); 29 | syn::custom_keyword!(replace_segment); 30 | } 31 | 32 | /// Parsed and validated set of `#[scale_info(...)]` attributes for an item. 33 | pub struct Attributes { 34 | bounds: Option, 35 | skip_type_params: Option, 36 | capture_docs: Option, 37 | crate_path: Option, 38 | replace_segments: Vec, 39 | } 40 | 41 | impl Attributes { 42 | /// Extract out `#[scale_info(...)]` attributes from an item. 43 | pub fn from_ast(item: &syn::DeriveInput) -> syn::Result { 44 | let mut bounds = None; 45 | let mut skip_type_params = None; 46 | let mut capture_docs = None; 47 | let mut crate_path = None; 48 | let mut replace_segments = Vec::new(); 49 | 50 | let attributes_parser = |input: &ParseBuffer| { 51 | let attrs = input.parse_terminated(ScaleInfoAttr::parse, Token![,])?; 52 | Ok(attrs) 53 | }; 54 | 55 | for attr in &item.attrs { 56 | if !attr.path().is_ident(SCALE_INFO) { 57 | continue; 58 | } 59 | let scale_info_attrs = attr.parse_args_with(attributes_parser)?; 60 | 61 | for scale_info_attr in scale_info_attrs { 62 | // check for duplicates 63 | match scale_info_attr { 64 | ScaleInfoAttr::Bounds(parsed_bounds) => { 65 | if bounds.is_some() { 66 | return Err(syn::Error::new( 67 | attr.span(), 68 | "Duplicate `bounds` attributes", 69 | )); 70 | } 71 | bounds = Some(parsed_bounds); 72 | } 73 | ScaleInfoAttr::SkipTypeParams(parsed_skip_type_params) => { 74 | if skip_type_params.is_some() { 75 | return Err(syn::Error::new( 76 | attr.span(), 77 | "Duplicate `skip_type_params` attributes", 78 | )); 79 | } 80 | skip_type_params = Some(parsed_skip_type_params); 81 | } 82 | ScaleInfoAttr::CaptureDocs(parsed_capture_docs) => { 83 | if capture_docs.is_some() { 84 | return Err(syn::Error::new( 85 | attr.span(), 86 | "Duplicate `capture_docs` attributes", 87 | )); 88 | } 89 | capture_docs = Some(parsed_capture_docs); 90 | } 91 | ScaleInfoAttr::CratePath(parsed_crate_path) => { 92 | if crate_path.is_some() { 93 | return Err(syn::Error::new( 94 | attr.span(), 95 | "Duplicate `crate` attributes", 96 | )); 97 | } 98 | 99 | crate_path = Some(parsed_crate_path); 100 | } 101 | ScaleInfoAttr::ReplaceSegment(replace_segment) => { 102 | replace_segments.push(replace_segment); 103 | } 104 | } 105 | } 106 | } 107 | 108 | // validate type params which do not appear in custom bounds but are not skipped. 109 | if let Some(ref bounds) = bounds { 110 | for type_param in item.generics.type_params() { 111 | if !bounds.contains_type_param(type_param) { 112 | let type_param_skipped = skip_type_params 113 | .as_ref() 114 | .map(|skip| skip.skip(type_param)) 115 | .unwrap_or(false); 116 | if !type_param_skipped { 117 | let msg = format!( 118 | "Type parameter requires a `TypeInfo` bound, so either: \n \ 119 | - add it to `#[scale_info(bounds({}: TypeInfo))]` \n \ 120 | - skip it with `#[scale_info(skip_type_params({}))]`", 121 | type_param.ident, type_param.ident 122 | ); 123 | return Err(syn::Error::new(type_param.span(), msg)); 124 | } 125 | } 126 | } 127 | } 128 | 129 | Ok(Self { 130 | bounds, 131 | skip_type_params, 132 | capture_docs, 133 | crate_path, 134 | replace_segments, 135 | }) 136 | } 137 | 138 | /// Get the `#[scale_info(bounds(...))]` attribute, if present. 139 | pub fn bounds(&self) -> Option<&BoundsAttr> { 140 | self.bounds.as_ref() 141 | } 142 | 143 | /// Get the `#[scale_info(skip_type_params(...))]` attribute, if present. 144 | pub fn skip_type_params(&self) -> Option<&SkipTypeParamsAttr> { 145 | self.skip_type_params.as_ref() 146 | } 147 | 148 | /// Returns the value of `#[scale_info(capture_docs = "..")]`. 149 | /// 150 | /// Defaults to `CaptureDocsAttr::Default` if the attribute is not present. 151 | pub fn capture_docs(&self) -> &CaptureDocsAttr { 152 | self.capture_docs 153 | .as_ref() 154 | .unwrap_or(&CaptureDocsAttr::Default) 155 | } 156 | 157 | /// Get the `#[scale_info(crate = path::to::crate)]` attribute, if present. 158 | pub fn crate_path(&self) -> Option<&CratePathAttr> { 159 | self.crate_path.as_ref() 160 | } 161 | 162 | /// Returns an iterator over the `#[scale_info(replace_segment("Hello", "world"))]` attributes. 163 | pub fn replace_segments(&self) -> impl Iterator { 164 | self.replace_segments.iter() 165 | } 166 | } 167 | 168 | /// Parsed representation of the `#[scale_info(bounds(...))]` attribute. 169 | #[derive(Clone)] 170 | pub struct BoundsAttr { 171 | predicates: Punctuated, 172 | } 173 | 174 | impl Parse for BoundsAttr { 175 | fn parse(input: &ParseBuffer) -> syn::Result { 176 | input.parse::()?; 177 | let content; 178 | syn::parenthesized!(content in input); 179 | let predicates = content.parse_terminated(syn::WherePredicate::parse, Token![,])?; 180 | Ok(Self { predicates }) 181 | } 182 | } 183 | 184 | impl BoundsAttr { 185 | /// Add the predicates defined in this attribute to the given `where` clause. 186 | pub fn extend_where_clause(&self, where_clause: &mut syn::WhereClause) { 187 | where_clause.predicates.extend(self.predicates.clone()); 188 | } 189 | 190 | /// Returns true if the given type parameter appears in the custom bounds attribute. 191 | pub fn contains_type_param(&self, type_param: &syn::TypeParam) -> bool { 192 | self.predicates.iter().any(|p| { 193 | if let syn::WherePredicate::Type(ty) = p { 194 | if let syn::Type::Path(ref path) = ty.bounded_ty { 195 | path.path.get_ident() == Some(&type_param.ident) 196 | } else { 197 | false 198 | } 199 | } else { 200 | false 201 | } 202 | }) 203 | } 204 | } 205 | 206 | /// Parsed representation of the `#[scale_info(skip_type_params(...))]` attribute. 207 | #[derive(Clone)] 208 | pub struct SkipTypeParamsAttr { 209 | type_params: Punctuated, 210 | } 211 | 212 | impl Parse for SkipTypeParamsAttr { 213 | fn parse(input: &ParseBuffer) -> syn::Result { 214 | input.parse::()?; 215 | let content; 216 | syn::parenthesized!(content in input); 217 | let type_params = content.parse_terminated(syn::TypeParam::parse, Token![,])?; 218 | Ok(Self { type_params }) 219 | } 220 | } 221 | 222 | impl SkipTypeParamsAttr { 223 | /// Returns `true` if the given type parameter should be skipped. 224 | pub fn skip(&self, type_param: &syn::TypeParam) -> bool { 225 | self.type_params 226 | .iter() 227 | .any(|tp| tp.ident == type_param.ident) 228 | } 229 | } 230 | 231 | /// Parsed representation of the `#[scale_info(capture_docs = "..")]` attribute. 232 | #[derive(Clone)] 233 | pub enum CaptureDocsAttr { 234 | Default, 235 | Always, 236 | Never, 237 | } 238 | 239 | impl Parse for CaptureDocsAttr { 240 | fn parse(input: &ParseBuffer) -> syn::Result { 241 | input.parse::()?; 242 | input.parse::()?; 243 | let capture_docs_lit = input.parse::()?; 244 | 245 | match capture_docs_lit.value().to_lowercase().as_str() { 246 | "default" => Ok(Self::Default), 247 | "always" => Ok(Self::Always), 248 | "never" => Ok(Self::Never), 249 | _ => Err(syn::Error::new_spanned( 250 | capture_docs_lit, 251 | r#"Invalid capture_docs value. Expected one of: "default", "always", "never" "#, 252 | )), 253 | } 254 | } 255 | } 256 | 257 | /// Parsed representation of the `#[scale_info(crate = "..")]` attribute. 258 | #[derive(Clone)] 259 | pub struct CratePathAttr { 260 | path: syn::Path, 261 | } 262 | 263 | impl CratePathAttr { 264 | pub fn path(&self) -> &syn::Path { 265 | &self.path 266 | } 267 | } 268 | 269 | impl Parse for CratePathAttr { 270 | fn parse(input: &ParseBuffer) -> syn::Result { 271 | input.parse::()?; 272 | input.parse::()?; 273 | let path = input.parse::()?; 274 | 275 | Ok(Self { path }) 276 | } 277 | } 278 | 279 | /// Parsed representation of the `#[scale_info(replace_segment("Hello", "world"))]` attribute. 280 | #[derive(Clone)] 281 | pub struct ReplaceSegment { 282 | search: LitStr, 283 | replace: LitStr, 284 | } 285 | 286 | impl ReplaceSegment { 287 | pub fn search(&self) -> &LitStr { 288 | &self.search 289 | } 290 | 291 | pub fn replace(&self) -> &LitStr { 292 | &self.replace 293 | } 294 | } 295 | 296 | impl Parse for ReplaceSegment { 297 | fn parse(input: &ParseBuffer) -> syn::Result { 298 | input.parse::()?; 299 | let content; 300 | syn::parenthesized!(content in input); 301 | 302 | let search = content.parse::()?; 303 | content.parse::()?; 304 | let replace = content.parse::()?; 305 | 306 | Ok(Self { search, replace }) 307 | } 308 | } 309 | 310 | /// Parsed representation of one of the `#[scale_info(..)]` attributes. 311 | pub enum ScaleInfoAttr { 312 | Bounds(BoundsAttr), 313 | SkipTypeParams(SkipTypeParamsAttr), 314 | CaptureDocs(CaptureDocsAttr), 315 | CratePath(CratePathAttr), 316 | ReplaceSegment(ReplaceSegment), 317 | } 318 | 319 | impl Parse for ScaleInfoAttr { 320 | fn parse(input: &ParseBuffer) -> syn::Result { 321 | let lookahead = input.lookahead1(); 322 | if lookahead.peek(keywords::bounds) { 323 | Ok(Self::Bounds(input.parse()?)) 324 | } else if lookahead.peek(keywords::skip_type_params) { 325 | Ok(Self::SkipTypeParams(input.parse()?)) 326 | } else if lookahead.peek(keywords::capture_docs) { 327 | Ok(Self::CaptureDocs(input.parse()?)) 328 | } else if lookahead.peek(Token![crate]) { 329 | Ok(Self::CratePath(input.parse()?)) 330 | } else if lookahead.peek(keywords::replace_segment) { 331 | Ok(Self::ReplaceSegment(input.parse()?)) 332 | } else { 333 | Err(lookahead.error()) 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | extern crate alloc; 16 | extern crate proc_macro; 17 | 18 | mod attr; 19 | mod trait_bounds; 20 | mod utils; 21 | 22 | use self::attr::{Attributes, CaptureDocsAttr, CratePathAttr}; 23 | use proc_macro::TokenStream; 24 | use proc_macro2::{Span, TokenStream as TokenStream2}; 25 | use quote::quote; 26 | use syn::{ 27 | parse::{Error, Result}, 28 | parse_quote, 29 | punctuated::Punctuated, 30 | token::Comma, 31 | visit_mut::VisitMut, 32 | Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Ident, Lifetime, 33 | }; 34 | 35 | #[proc_macro_derive(TypeInfo, attributes(scale_info, codec))] 36 | pub fn type_info(input: TokenStream) -> TokenStream { 37 | match generate(input.into()) { 38 | Ok(output) => output.into(), 39 | Err(err) => err.to_compile_error().into(), 40 | } 41 | } 42 | 43 | fn generate(input: TokenStream2) -> Result { 44 | let type_info_impl = TypeInfoImpl::parse(input)?; 45 | let type_info_impl_toks = type_info_impl.expand()?; 46 | Ok(quote! { 47 | #[allow(non_upper_case_globals, deprecated, unused_attributes, unused_qualifications)] 48 | const _: () = { 49 | #type_info_impl_toks; 50 | }; 51 | }) 52 | } 53 | 54 | struct TypeInfoImpl { 55 | ast: DeriveInput, 56 | attrs: Attributes, 57 | } 58 | 59 | impl TypeInfoImpl { 60 | fn parse(input: TokenStream2) -> Result { 61 | let ast: DeriveInput = syn::parse2(input)?; 62 | let attrs = attr::Attributes::from_ast(&ast)?; 63 | 64 | Ok(Self { ast, attrs }) 65 | } 66 | 67 | fn expand(&self) -> Result { 68 | let ident = &self.ast.ident; 69 | let ident_str = ident.to_string(); 70 | let scale_info = crate_path(self.attrs.crate_path())?; 71 | 72 | let where_clause = trait_bounds::make_where_clause( 73 | &self.attrs, 74 | ident, 75 | &self.ast.generics, 76 | &self.ast.data, 77 | &scale_info, 78 | )?; 79 | 80 | let (impl_generics, ty_generics, _) = self.ast.generics.split_for_impl(); 81 | 82 | let type_params = self.ast.generics.type_params().map(|tp| { 83 | let ty_ident = &tp.ident; 84 | let ty = if self 85 | .attrs 86 | .skip_type_params() 87 | .map_or(true, |skip| !skip.skip(tp)) 88 | { 89 | quote! { ::core::option::Option::Some(#scale_info::meta_type::<#ty_ident>()) } 90 | } else { 91 | quote! { ::core::option::Option::None } 92 | }; 93 | quote! { 94 | #scale_info::TypeParameter::new(::core::stringify!(#ty_ident), #ty) 95 | } 96 | }); 97 | 98 | let build_type = match &self.ast.data { 99 | Data::Struct(ref s) => self.generate_composite_type(s, &scale_info), 100 | Data::Enum(ref e) => self.generate_variant_type(e, &scale_info), 101 | Data::Union(_) => return Err(Error::new_spanned(&self.ast, "Unions not supported")), 102 | }; 103 | let docs = self.generate_docs(&self.ast.attrs); 104 | 105 | let replaces = self.attrs.replace_segments().map(|r| { 106 | let search = r.search(); 107 | let replace = r.replace(); 108 | 109 | quote!(( #search, #replace )) 110 | }); 111 | 112 | Ok(quote! { 113 | #[automatically_derived] 114 | impl #impl_generics #scale_info::TypeInfo for #ident #ty_generics #where_clause { 115 | type Identity = Self; 116 | fn type_info() -> #scale_info::Type { 117 | #scale_info::Type::builder() 118 | .path(#scale_info::Path::new_with_replace( 119 | #ident_str, 120 | ::core::module_path!(), 121 | &[ #( #replaces ),* ] 122 | )) 123 | .type_params(#scale_info::prelude::vec![ #( #type_params ),* ]) 124 | #docs 125 | .#build_type 126 | } 127 | } 128 | }) 129 | } 130 | 131 | fn generate_composite_type( 132 | &self, 133 | data_struct: &DataStruct, 134 | scale_info: &syn::Path, 135 | ) -> TokenStream2 { 136 | let fields = match data_struct.fields { 137 | Fields::Named(ref fs) => { 138 | let fields = self.generate_fields(&fs.named); 139 | quote! { named()#( #fields )* } 140 | } 141 | Fields::Unnamed(ref fs) => { 142 | let fields = self.generate_fields(&fs.unnamed); 143 | quote! { unnamed()#( #fields )* } 144 | } 145 | Fields::Unit => { 146 | quote! { 147 | unit() 148 | } 149 | } 150 | }; 151 | 152 | quote! { 153 | composite(#scale_info::build::Fields::#fields) 154 | } 155 | } 156 | 157 | fn generate_fields(&self, fields: &Punctuated) -> Vec { 158 | fields 159 | .iter() 160 | .filter(|f| !utils::should_skip(&f.attrs)) 161 | .map(|f| { 162 | let (ty, ident) = (&f.ty, &f.ident); 163 | // Replace any field lifetime params with `static to prevent "unnecessary lifetime parameter" 164 | // warning. Any lifetime parameters are specified as 'static in the type of the impl. 165 | struct StaticLifetimesReplace; 166 | impl VisitMut for StaticLifetimesReplace { 167 | fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { 168 | *lifetime = parse_quote!('static) 169 | } 170 | } 171 | let mut ty = match ty { 172 | // When a type is specified as part of a `macro_rules!`, the tokens passed to 173 | // the `TypeInfo` derive macro are a type `Group`, which is pretty printed with 174 | // invisible delimiters e.g. /*«*/ bool /*»*/. To avoid printing the delimiters 175 | // the inner type element is extracted. 176 | syn::Type::Group(group) => (*group.elem).clone(), 177 | _ => ty.clone(), 178 | }; 179 | StaticLifetimesReplace.visit_type_mut(&mut ty); 180 | 181 | let type_name = clean_type_string("e!(#ty).to_string()); 182 | let docs = self.generate_docs(&f.attrs); 183 | let type_of_method = if utils::is_compact(f) { 184 | quote!(compact) 185 | } else { 186 | quote!(ty) 187 | }; 188 | let name = match utils::maybe_renamed(f) { 189 | Some(ident) => quote!(.name(#ident)), 190 | None => ident 191 | .as_ref() 192 | .map(|ident| quote!(.name(::core::stringify!(#ident)))) 193 | .unwrap_or(quote!()), 194 | }; 195 | quote!( 196 | .field(|f| f 197 | .#type_of_method::<#ty>() 198 | #name 199 | .type_name(#type_name) 200 | #docs 201 | ) 202 | ) 203 | }) 204 | .collect() 205 | } 206 | 207 | fn generate_variant_type(&self, data_enum: &DataEnum, scale_info: &syn::Path) -> TokenStream2 { 208 | let variants = &data_enum.variants; 209 | 210 | let variants = variants 211 | .into_iter() 212 | .filter(|v| !utils::should_skip(&v.attrs)) 213 | .enumerate() 214 | .map(|(i, v)| { 215 | let ident = &v.ident; 216 | let v_name = quote! {::core::stringify!(#ident) }; 217 | let docs = self.generate_docs(&v.attrs); 218 | let index = utils::variant_index(v, i); 219 | 220 | let fields = match v.fields { 221 | Fields::Named(ref fs) => { 222 | let fields = self.generate_fields(&fs.named); 223 | Some(quote! { 224 | .fields(#scale_info::build::Fields::named() 225 | #( #fields )* 226 | ) 227 | }) 228 | } 229 | Fields::Unnamed(ref fs) => { 230 | let fields = self.generate_fields(&fs.unnamed); 231 | Some(quote! { 232 | .fields(#scale_info::build::Fields::unnamed() 233 | #( #fields )* 234 | ) 235 | }) 236 | } 237 | Fields::Unit => None, 238 | }; 239 | 240 | quote! { 241 | .variant(#v_name, |v| 242 | v 243 | .index(#index as ::core::primitive::u8) 244 | #fields 245 | #docs 246 | ) 247 | } 248 | }); 249 | quote! { 250 | variant( 251 | #scale_info::build::Variants::new() 252 | #( #variants )* 253 | ) 254 | } 255 | } 256 | 257 | fn generate_docs(&self, attrs: &[syn::Attribute]) -> Option { 258 | let docs_builder_fn = match self.attrs.capture_docs() { 259 | CaptureDocsAttr::Never => None, // early return if we never capture docs. 260 | CaptureDocsAttr::Default => Some(quote!(docs)), 261 | CaptureDocsAttr::Always => Some(quote!(docs_always)), 262 | }?; 263 | 264 | let docs = attrs 265 | .iter() 266 | .filter_map(|attr| { 267 | if !attr.path().is_ident("doc") { 268 | return None; 269 | } 270 | let syn::Meta::NameValue(nv) = &attr.meta else { 271 | return None; 272 | }; 273 | let syn::Expr::Lit(syn::ExprLit { 274 | lit: syn::Lit::Str(s), 275 | .. 276 | }) = &nv.value 277 | else { 278 | return None; 279 | }; 280 | 281 | let lit_value = s.value(); 282 | let stripped = lit_value.strip_prefix(' ').unwrap_or(&lit_value); 283 | let lit: syn::Lit = parse_quote!(#stripped); 284 | Some(lit) 285 | }) 286 | .collect::>(); 287 | 288 | if !docs.is_empty() { 289 | Some(quote! { 290 | .#docs_builder_fn(&[ #( #docs ),* ]) 291 | }) 292 | } else { 293 | None 294 | } 295 | } 296 | } 297 | 298 | /// Get the name of a crate, to be robust against renamed dependencies. 299 | fn crate_name_path(name: &str) -> Result { 300 | proc_macro_crate::crate_name(name) 301 | .map(|crate_name| { 302 | use proc_macro_crate::FoundCrate::*; 303 | match crate_name { 304 | Itself => Ident::new("self", Span::call_site()).into(), 305 | Name(name) => { 306 | let crate_ident = Ident::new(&name, Span::call_site()); 307 | parse_quote!( ::#crate_ident ) 308 | } 309 | } 310 | }) 311 | .map_err(|e| syn::Error::new(Span::call_site(), &e)) 312 | } 313 | 314 | fn crate_path(crate_path_attr: Option<&CratePathAttr>) -> Result { 315 | crate_path_attr 316 | .map(|path_attr| Ok(path_attr.path().clone())) 317 | .unwrap_or_else(|| crate_name_path("scale-info")) 318 | } 319 | 320 | fn clean_type_string(input: &str) -> String { 321 | input 322 | .replace(" ::", "::") 323 | .replace(":: ", "::") 324 | .replace(" ,", ",") 325 | .replace(" ;", ";") 326 | .replace(" [", "[") 327 | .replace("[ ", "[") 328 | .replace(" ]", "]") 329 | .replace(" (", "(") 330 | // put back a space so that `a: (u8, (bool, u8))` isn't turned into `a: (u8,(bool, u8))` 331 | .replace(",(", ", (") 332 | .replace("( ", "(") 333 | .replace(" )", ")") 334 | .replace(" <", "<") 335 | .replace("< ", "<") 336 | .replace(" >", ">") 337 | .replace("& \'", "&'") 338 | } 339 | -------------------------------------------------------------------------------- /derive/src/trait_bounds.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use alloc::vec::Vec; 16 | use proc_macro2::Ident; 17 | use syn::{ 18 | parse_quote, 19 | punctuated::Punctuated, 20 | spanned::Spanned, 21 | visit::{self, Visit}, 22 | Generics, Result, Type, TypePath, WhereClause, 23 | }; 24 | 25 | use crate::{attr::Attributes, utils}; 26 | 27 | /// Generates a where clause for a `TypeInfo` impl, adding `TypeInfo + 'static` bounds to all 28 | /// relevant generic types including associated types (e.g. `T::A: TypeInfo`), correctly dealing 29 | /// with self-referential types. 30 | /// 31 | /// # Effect of attributes 32 | /// 33 | /// `#[scale_info(skip_type_params(..))]` 34 | /// 35 | /// Will not add `TypeInfo` bounds for any type parameters skipped via this attribute. 36 | /// 37 | /// `#[scale_info(bounds(..))]` 38 | /// 39 | /// Replaces *all* auto-generated trait bounds with the user-defined ones. 40 | pub fn make_where_clause<'a>( 41 | attrs: &'a Attributes, 42 | input_ident: &'a Ident, 43 | generics: &'a Generics, 44 | data: &'a syn::Data, 45 | scale_info: &syn::Path, 46 | ) -> Result { 47 | let mut where_clause = generics 48 | .where_clause 49 | .clone() 50 | .unwrap_or_else(|| WhereClause { 51 | where_token: ::default(), 52 | predicates: Punctuated::new(), 53 | }); 54 | 55 | // Use custom bounds as where clause. 56 | if let Some(custom_bounds) = attrs.bounds() { 57 | custom_bounds.extend_where_clause(&mut where_clause); 58 | 59 | // `'static` lifetime bounds are always required for type parameters, because of the 60 | // requirement on `std::any::TypeId::of` for any field type constructor. 61 | for type_param in generics.type_params() { 62 | let ident = &type_param.ident; 63 | where_clause.predicates.push(parse_quote!(#ident: 'static)) 64 | } 65 | 66 | return Ok(where_clause); 67 | } 68 | 69 | for lifetime in generics.lifetimes() { 70 | where_clause 71 | .predicates 72 | .push(parse_quote!(#lifetime: 'static)) 73 | } 74 | 75 | let ty_params_ids = generics 76 | .type_params() 77 | .map(|type_param| type_param.ident.clone()) 78 | .collect::>(); 79 | 80 | if ty_params_ids.is_empty() { 81 | return Ok(where_clause); 82 | } 83 | 84 | let types = collect_types_to_bind(input_ident, data, &ty_params_ids)?; 85 | 86 | types.into_iter().for_each(|(ty, is_compact)| { 87 | if is_compact { 88 | where_clause 89 | .predicates 90 | .push(parse_quote!(#ty : #scale_info :: scale::HasCompact)); 91 | } else { 92 | where_clause 93 | .predicates 94 | .push(parse_quote!(#ty : #scale_info ::TypeInfo + 'static)); 95 | } 96 | }); 97 | 98 | generics.type_params().for_each(|type_param| { 99 | let ident = type_param.ident.clone(); 100 | let mut bounds = type_param.bounds.clone(); 101 | if attrs 102 | .skip_type_params() 103 | .map_or(true, |skip| !skip.skip(type_param)) 104 | { 105 | bounds.push(parse_quote!(#scale_info ::TypeInfo)); 106 | } 107 | bounds.push(parse_quote!('static)); 108 | where_clause 109 | .predicates 110 | .push(parse_quote!( #ident : #bounds)); 111 | }); 112 | 113 | Ok(where_clause) 114 | } 115 | 116 | /// Visits the ast and checks if the given type contains one of the given 117 | /// idents. 118 | fn type_contains_idents(ty: &Type, idents: &[Ident]) -> bool { 119 | struct ContainIdents<'a> { 120 | result: bool, 121 | idents: &'a [Ident], 122 | } 123 | 124 | impl<'ast> Visit<'ast> for ContainIdents<'_> { 125 | fn visit_ident(&mut self, i: &'ast Ident) { 126 | if self.idents.iter().any(|id| id == i) { 127 | self.result = true; 128 | } 129 | } 130 | } 131 | 132 | let mut visitor = ContainIdents { 133 | result: false, 134 | idents, 135 | }; 136 | visitor.visit_type(ty); 137 | visitor.result 138 | } 139 | 140 | /// Checks if the given type or any containing type path starts with the given ident. 141 | fn type_or_sub_type_path_starts_with_ident(ty: &Type, ident: &Ident) -> bool { 142 | // Visits the ast and checks if the a type path starts with the given ident. 143 | struct TypePathStartsWithIdent<'a> { 144 | result: bool, 145 | ident: &'a Ident, 146 | } 147 | 148 | impl<'ast> Visit<'ast> for TypePathStartsWithIdent<'_> { 149 | fn visit_type_path(&mut self, i: &'ast TypePath) { 150 | if i.qself.is_none() { 151 | if let Some(segment) = i.path.segments.first() { 152 | if &segment.ident == self.ident { 153 | self.result = true; 154 | return; 155 | } 156 | } 157 | } 158 | visit::visit_type_path(self, i); 159 | } 160 | } 161 | 162 | let mut visitor = TypePathStartsWithIdent { 163 | result: false, 164 | ident, 165 | }; 166 | visitor.visit_type(ty); 167 | visitor.result 168 | } 169 | 170 | /// Returns all types that must be added to the where clause with a boolean 171 | /// indicating if the field is [`scale::Compact`] or not. 172 | fn collect_types_to_bind( 173 | input_ident: &Ident, 174 | data: &syn::Data, 175 | ty_params: &[Ident], 176 | ) -> Result> { 177 | let types_from_fields = |fields: &Punctuated| -> Vec<(Type, bool)> { 178 | fields 179 | .iter() 180 | .filter(|field| { 181 | // Only add a bound if the type uses a generic. 182 | type_contains_idents(&field.ty, ty_params) 183 | && 184 | // Remove all remaining types that start/contain the input ident 185 | // to not have them in the where clause. 186 | !type_or_sub_type_path_starts_with_ident(&field.ty, input_ident) 187 | }) 188 | .map(|f| (f.ty.clone(), utils::is_compact(f))) 189 | .collect() 190 | }; 191 | 192 | let types = match *data { 193 | syn::Data::Struct(ref data) => match &data.fields { 194 | syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) 195 | | syn::Fields::Unnamed(syn::FieldsUnnamed { 196 | unnamed: fields, .. 197 | }) => types_from_fields(fields), 198 | syn::Fields::Unit => Vec::new(), 199 | }, 200 | 201 | syn::Data::Enum(ref data) => data 202 | .variants 203 | .iter() 204 | .flat_map(|variant| match &variant.fields { 205 | syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) 206 | | syn::Fields::Unnamed(syn::FieldsUnnamed { 207 | unnamed: fields, .. 208 | }) => types_from_fields(fields), 209 | syn::Fields::Unit => Vec::new(), 210 | }) 211 | .collect(), 212 | 213 | syn::Data::Union(ref data) => { 214 | return Err(syn::Error::new( 215 | data.union_token.span(), 216 | "Union types are not supported.", 217 | )) 218 | } 219 | }; 220 | 221 | Ok(types) 222 | } 223 | -------------------------------------------------------------------------------- /derive/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Utility methods to work with `SCALE` attributes relevant for the `TypeInfo` derive.. 16 | //! 17 | //! NOTE: The code here is copied verbatim from `parity-scale-codec-derive`. 18 | 19 | use proc_macro2::TokenStream; 20 | use quote::quote; 21 | use syn::{ 22 | parse::Parse, spanned::Spanned, AttrStyle, Attribute, Expr, ExprLit, Lit, Meta, Variant, 23 | }; 24 | 25 | /// Look for a `#[codec(index = $int)]` attribute on a variant. If no attribute 26 | /// is found, fall back to the discriminant or just the variant index. 27 | pub fn variant_index(v: &Variant, i: usize) -> TokenStream { 28 | // first look for an `index` attribute… 29 | let index = maybe_index(v); 30 | // …then fallback to discriminant or just index 31 | index.map(|i| quote! { #i }).unwrap_or_else(|| { 32 | v.discriminant 33 | .as_ref() 34 | .map(|(_, ref expr)| quote! { #expr }) 35 | .unwrap_or_else(|| quote! { #i }) 36 | }) 37 | } 38 | 39 | /// Look for a `#[codec(index = $int)]` outer attribute on a variant. 40 | /// If found, it is expected to be a parseable as a `u8` (panics otherwise). 41 | pub fn maybe_index(variant: &Variant) -> Option { 42 | let outer_attrs = variant 43 | .attrs 44 | .iter() 45 | .filter(|attr| attr.style == AttrStyle::Outer); 46 | 47 | codec_meta_item(outer_attrs, |meta| { 48 | if let Meta::NameValue(ref nv) = meta { 49 | if nv.path.is_ident("index") { 50 | if let Expr::Lit(ExprLit { 51 | lit: Lit::Int(ref v), 52 | .. 53 | }) = nv.value 54 | { 55 | let byte = v 56 | .base10_parse::() 57 | .expect("Internal error. `#[codec(index = …)]` attribute syntax must be checked in `parity-scale-codec`. This is a bug."); 58 | return Some(byte); 59 | } 60 | } 61 | } 62 | 63 | None 64 | }) 65 | } 66 | 67 | /// Look for a `#[codec(compact)]` outer attribute on the given `Field`. 68 | pub fn is_compact(field: &syn::Field) -> bool { 69 | let outer_attrs = field 70 | .attrs 71 | .iter() 72 | .filter(|attr| attr.style == AttrStyle::Outer); 73 | codec_meta_item(outer_attrs, |meta| { 74 | if let Meta::Path(ref path) = meta { 75 | if path.is_ident("compact") { 76 | return Some(()); 77 | } 78 | } 79 | 80 | None 81 | }) 82 | .is_some() 83 | } 84 | 85 | /// Look for a `#[codec(skip)]` in the given attributes. 86 | pub fn should_skip(attrs: &[Attribute]) -> bool { 87 | codec_meta_item(attrs.iter(), |meta| { 88 | if let Meta::Path(ref path) = meta { 89 | if path.is_ident("skip") { 90 | return Some(path.span()); 91 | } 92 | } 93 | 94 | None 95 | }) 96 | .is_some() 97 | } 98 | 99 | /// Look for a `#[scale_info(rename = $str)]` outer attribute on the given `Field`. 100 | pub fn maybe_renamed(field: &syn::Field) -> Option { 101 | let outer_attrs = field 102 | .attrs 103 | .iter() 104 | .filter(|attr| attr.style == AttrStyle::Outer); 105 | scale_info_meta_item(outer_attrs, |meta| { 106 | if let Meta::NameValue(ref nv) = meta { 107 | if nv.path.is_ident("rename") { 108 | if let Expr::Lit(ExprLit { 109 | lit: Lit::Str(ref v), 110 | .. 111 | }) = nv.value 112 | { 113 | return Some(v.value()); 114 | } 115 | } 116 | } 117 | 118 | None 119 | }) 120 | } 121 | 122 | fn codec_meta_item<'a, F, R, I, M>(itr: I, pred: F) -> Option 123 | where 124 | F: FnMut(M) -> Option + Clone, 125 | I: Iterator, 126 | M: Parse, 127 | { 128 | find_meta_item("codec", itr, pred) 129 | } 130 | 131 | fn scale_info_meta_item<'a, F, R, I, M>(itr: I, pred: F) -> Option 132 | where 133 | F: FnMut(M) -> Option + Clone, 134 | I: Iterator, 135 | M: Parse, 136 | { 137 | find_meta_item("scale_info", itr, pred) 138 | } 139 | 140 | fn find_meta_item<'a, F, R, I, M>(kind: &str, mut itr: I, mut pred: F) -> Option 141 | where 142 | F: FnMut(M) -> Option + Clone, 143 | I: Iterator, 144 | M: Parse, 145 | { 146 | itr.find_map(|attr| { 147 | attr.path() 148 | .is_ident(kind) 149 | .then(|| pred(attr.parse_args().ok()?)) 150 | .flatten() 151 | }) 152 | } 153 | -------------------------------------------------------------------------------- /src/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Builders for defining metadata for variant types (enums), and composite types (structs). 16 | //! They are designed to allow only construction of valid definitions. 17 | //! 18 | //! In most cases we recommend using the `scale-info-derive` crate to auto generate the builder 19 | //! constructions. 20 | //! 21 | //! # Examples 22 | //! 23 | //! ## Generic struct 24 | //! ``` 25 | //! # use scale_info::{build::Fields, type_params, MetaType, Path, Type, TypeInfo}; 26 | //! struct Foo { 27 | //! bar: T, 28 | //! data: u64, 29 | //! } 30 | //! 31 | //! impl TypeInfo for Foo 32 | //! where 33 | //! T: TypeInfo + 'static, 34 | //! { 35 | //! type Identity = Self; 36 | //! 37 | //! fn type_info() -> Type { 38 | //! Type::builder() 39 | //! .path(Path::new("Foo", module_path!())) 40 | //! .type_params(type_params!(T)) 41 | //! .composite(Fields::named() 42 | //! .field(|f| f.ty::().name("bar").type_name("T")) 43 | //! .field(|f| f.ty::().name("data").type_name("u64")) 44 | //! ) 45 | //! } 46 | //! } 47 | //! ``` 48 | //! ## Tuple struct 49 | //! ``` 50 | //! # use scale_info::{build::Fields, MetaType, Path, Type, TypeInfo}; 51 | //! struct Foo(u32, bool); 52 | //! 53 | //! impl TypeInfo for Foo { 54 | //! type Identity = Self; 55 | //! 56 | //! fn type_info() -> Type { 57 | //! Type::builder() 58 | //! .path(Path::new("Foo", module_path!())) 59 | //! .composite(Fields::unnamed() 60 | //! .field(|f| f.ty::().type_name("u32")) 61 | //! .field(|f| f.ty::().type_name("bool")) 62 | //! ) 63 | //! } 64 | //! } 65 | //! ``` 66 | //! ## Enum with fields 67 | //! ``` 68 | //! # use scale_info::{build::{Fields, Variants}, type_params, MetaType, Path, Type, TypeInfo, Variant}; 69 | //! enum Foo{ 70 | //! A(T), 71 | //! B { f: u32 }, 72 | //! C, 73 | //! } 74 | //! 75 | //! impl TypeInfo for Foo 76 | //! where 77 | //! T: TypeInfo + 'static, 78 | //! { 79 | //! type Identity = Self; 80 | //! 81 | //! fn type_info() -> Type { 82 | //! Type::builder() 83 | //! .path(Path::new("Foo", module_path!())) 84 | //! .type_params(type_params!(T)) 85 | //! .variant( 86 | //! Variants::new() 87 | //! .variant("A", |v| v 88 | //! .index(0) 89 | //! .fields(Fields::unnamed().field(|f| f.ty::().type_name("T"))) 90 | //! ) 91 | //! .variant("B", |v| v 92 | //! .index(1) 93 | //! .fields(Fields::named().field(|f| f.ty::().name("f").type_name("u32"))) 94 | //! ) 95 | //! .variant_unit("A", 2) 96 | //! ) 97 | //! } 98 | //! } 99 | //! ``` 100 | //! ## Enum without fields, aka C-style enums. 101 | //! ``` 102 | //! # use scale_info::{build::{Fields, Variants}, MetaType, Path, Type, TypeInfo, Variant}; 103 | //! enum Foo { 104 | //! A, 105 | //! B, 106 | //! C = 33, 107 | //! } 108 | //! 109 | //! impl TypeInfo for Foo { 110 | //! type Identity = Self; 111 | //! 112 | //! fn type_info() -> Type { 113 | //! Type::builder() 114 | //! .path(Path::new("Foo", module_path!())) 115 | //! .variant( 116 | //! Variants::new() 117 | //! .variant("A", |v| v.index(1)) 118 | //! .variant("B", |v| v.index(2)) 119 | //! .variant("C", |v| v.index(33)) 120 | //! ) 121 | //! } 122 | //! } 123 | //! ``` 124 | 125 | use crate::prelude::{marker::PhantomData, vec::Vec}; 126 | 127 | use crate::{ 128 | form::{Form, MetaForm, PortableForm}, 129 | Field, MetaType, Path, Type, TypeDef, TypeDefComposite, TypeDefVariant, TypeInfo, 130 | TypeParameter, Variant, 131 | }; 132 | 133 | /// State types for type builders which require a Path. 134 | pub mod state { 135 | /// State where the builder has not assigned a Path to the type 136 | pub enum PathNotAssigned {} 137 | /// State where the builder has assigned a Path to the type 138 | pub enum PathAssigned {} 139 | } 140 | 141 | /// Builds a [`Type`](`crate::Type`) 142 | #[must_use] 143 | pub struct TypeBuilder { 144 | path: Option>, 145 | type_params: Vec>, 146 | docs: Vec, 147 | marker: PhantomData (F, S)>, 148 | } 149 | 150 | impl Default for TypeBuilder { 151 | fn default() -> Self { 152 | TypeBuilder { 153 | path: Default::default(), 154 | type_params: Default::default(), 155 | docs: Default::default(), 156 | marker: Default::default(), 157 | } 158 | } 159 | } 160 | 161 | impl TypeBuilder { 162 | /// Set the Path for the type 163 | pub fn path(self, path: Path) -> TypeBuilder { 164 | TypeBuilder { 165 | path: Some(path), 166 | type_params: self.type_params, 167 | docs: self.docs, 168 | marker: Default::default(), 169 | } 170 | } 171 | } 172 | 173 | impl TypeBuilder { 174 | fn build(self, type_def: D) -> Type 175 | where 176 | D: Into>, 177 | { 178 | let path = self.path.expect("Path not assigned"); 179 | Type::new(path, self.type_params, type_def, self.docs) 180 | } 181 | 182 | /// Construct a "variant" type i.e an `enum` 183 | pub fn variant(self, builder: Variants) -> Type { 184 | self.build(builder.finalize()) 185 | } 186 | 187 | /// Construct a "composite" type i.e. a `struct` 188 | pub fn composite(self, fields: FieldsBuilder) -> Type { 189 | self.build(TypeDefComposite::new(fields.finalize())) 190 | } 191 | } 192 | 193 | impl TypeBuilder { 194 | /// Set the type parameters if it's a generic type 195 | pub fn type_params(mut self, type_params: I) -> Self 196 | where 197 | I: IntoIterator>, 198 | { 199 | self.type_params = type_params.into_iter().collect(); 200 | self 201 | } 202 | } 203 | 204 | impl TypeBuilder { 205 | #[cfg(feature = "docs")] 206 | /// Set the type documentation (for types in portable form). 207 | pub fn docs_portable(mut self, docs: I) -> Self 208 | where 209 | I: IntoIterator::String>, 210 | { 211 | self.docs = docs.into_iter().collect(); 212 | self 213 | } 214 | } 215 | 216 | impl TypeBuilder { 217 | #[cfg(feature = "docs")] 218 | /// Set the type documentation 219 | pub fn docs(mut self, docs: &[&'static str]) -> Self { 220 | self.docs = docs.to_vec(); 221 | self 222 | } 223 | 224 | #[cfg(not(feature = "docs"))] 225 | #[inline] 226 | /// Doc capture is not enabled via the "docs" feature so this is a no-op. 227 | pub fn docs(self, _docs: &'static [&'static str]) -> Self { 228 | self 229 | } 230 | 231 | /// Set the type documentation, always captured even if the "docs" feature is not enabled. 232 | pub fn docs_always(mut self, docs: &[&'static str]) -> Self { 233 | self.docs = docs.to_vec(); 234 | self 235 | } 236 | } 237 | 238 | /// A fields builder has no fields (e.g. a unit struct) 239 | pub enum NoFields {} 240 | /// A fields builder only allows named fields (e.g. a struct) 241 | pub enum NamedFields {} 242 | /// A fields builder only allows unnamed fields (e.g. a tuple) 243 | pub enum UnnamedFields {} 244 | 245 | /// Provides FieldsBuilder constructors 246 | pub struct Fields(PhantomData F>); 247 | 248 | impl Fields { 249 | /// The type construct has no fields 250 | pub fn unit() -> FieldsBuilder { 251 | FieldsBuilder::::default() 252 | } 253 | 254 | /// Fields for a type construct with named fields 255 | pub fn named() -> FieldsBuilder { 256 | FieldsBuilder::default() 257 | } 258 | 259 | /// Fields for a type construct with unnamed fields 260 | pub fn unnamed() -> FieldsBuilder { 261 | FieldsBuilder::default() 262 | } 263 | } 264 | 265 | /// Build a set of either all named (e.g. for a struct) or all unnamed (e.g. for a tuple struct) 266 | #[must_use] 267 | pub struct FieldsBuilder { 268 | fields: Vec>, 269 | marker: PhantomData T>, 270 | } 271 | 272 | impl Default for FieldsBuilder { 273 | fn default() -> Self { 274 | Self { 275 | fields: Vec::new(), 276 | marker: Default::default(), 277 | } 278 | } 279 | } 280 | 281 | impl FieldsBuilder { 282 | /// Complete building and return the set of fields 283 | pub fn finalize(self) -> Vec> { 284 | self.fields 285 | } 286 | } 287 | 288 | impl FieldsBuilder { 289 | fn push_field(mut self, field: Field) -> Self { 290 | // filter out fields of PhantomData 291 | if !field.ty.is_phantom() { 292 | self.fields.push(field); 293 | } 294 | self 295 | } 296 | } 297 | 298 | impl FieldsBuilder { 299 | /// Add a named field constructed using the builder. 300 | pub fn field(self, builder: B) -> Self 301 | where 302 | B: Fn( 303 | FieldBuilder, 304 | ) 305 | -> FieldBuilder, 306 | { 307 | let builder = builder(FieldBuilder::new()); 308 | self.push_field(builder.finalize()) 309 | } 310 | } 311 | 312 | impl FieldsBuilder { 313 | /// Add an unnamed field constructed using the builder. 314 | pub fn field(self, builder: B) -> Self 315 | where 316 | B: Fn( 317 | FieldBuilder, 318 | ) 319 | -> FieldBuilder, 320 | { 321 | let builder = builder(FieldBuilder::new()); 322 | self.push_field(builder.finalize()) 323 | } 324 | } 325 | 326 | impl FieldsBuilder { 327 | fn push_field(mut self, field: Field) -> Self { 328 | self.fields.push(field); 329 | self 330 | } 331 | } 332 | 333 | impl FieldsBuilder { 334 | /// Add a named field constructed using the builder. 335 | pub fn field_portable(self, builder: B) -> Self 336 | where 337 | B: Fn( 338 | FieldBuilder, 339 | ) 340 | -> FieldBuilder, 341 | { 342 | let builder = builder(FieldBuilder::new()); 343 | self.push_field(builder.finalize()) 344 | } 345 | } 346 | 347 | impl FieldsBuilder { 348 | /// Add an unnamed field constructed using the builder. 349 | pub fn field_portable(self, builder: B) -> Self 350 | where 351 | B: Fn( 352 | FieldBuilder, 353 | ) -> FieldBuilder< 354 | PortableForm, 355 | field_state::NameNotAssigned, 356 | field_state::TypeAssigned, 357 | >, 358 | { 359 | let builder = builder(FieldBuilder::new()); 360 | self.push_field(builder.finalize()) 361 | } 362 | } 363 | 364 | /// Type states for building a field. 365 | pub mod field_state { 366 | /// A name has not been assigned to the field. 367 | pub enum NameNotAssigned {} 368 | /// A name has been assigned to the field. 369 | pub enum NameAssigned {} 370 | /// A type has not been assigned to the field. 371 | pub enum TypeNotAssigned {} 372 | /// A type has been assigned to the field. 373 | pub enum TypeAssigned {} 374 | } 375 | 376 | /// Construct a valid [`Field`]. 377 | #[must_use] 378 | pub struct FieldBuilder< 379 | F: Form = MetaForm, 380 | N = field_state::NameNotAssigned, 381 | T = field_state::TypeNotAssigned, 382 | > { 383 | name: Option, 384 | ty: Option, 385 | type_name: Option, 386 | docs: Vec, 387 | marker: PhantomData (N, T)>, 388 | } 389 | 390 | impl Default for FieldBuilder { 391 | fn default() -> Self { 392 | FieldBuilder { 393 | name: Default::default(), 394 | ty: Default::default(), 395 | type_name: Default::default(), 396 | docs: Default::default(), 397 | marker: Default::default(), 398 | } 399 | } 400 | } 401 | 402 | impl FieldBuilder { 403 | /// Create a new FieldBuilder. 404 | pub fn new() -> Self { 405 | Default::default() 406 | } 407 | } 408 | 409 | impl FieldBuilder { 410 | /// Initialize the field name. 411 | pub fn name(self, name: F::String) -> FieldBuilder { 412 | FieldBuilder { 413 | name: Some(name), 414 | ty: self.ty, 415 | type_name: self.type_name, 416 | docs: self.docs, 417 | marker: PhantomData, 418 | } 419 | } 420 | } 421 | 422 | impl FieldBuilder { 423 | /// Initialize the type of the field. 424 | pub fn ty(self) -> FieldBuilder 425 | where 426 | TY: TypeInfo + 'static + ?Sized, 427 | { 428 | FieldBuilder { 429 | name: self.name, 430 | ty: Some(MetaType::new::()), 431 | type_name: self.type_name, 432 | docs: self.docs, 433 | marker: PhantomData, 434 | } 435 | } 436 | 437 | /// Initializes the type of the field as a compact type. 438 | pub fn compact(self) -> FieldBuilder 439 | where 440 | TY: scale::HasCompact + TypeInfo + 'static, 441 | { 442 | FieldBuilder { 443 | name: self.name, 444 | ty: Some(MetaType::new::>()), 445 | type_name: self.type_name, 446 | docs: self.docs, 447 | marker: PhantomData, 448 | } 449 | } 450 | } 451 | 452 | impl FieldBuilder { 453 | /// Initialize the type of the field. 454 | pub fn ty(self, ty: T) -> FieldBuilder 455 | where 456 | T: Into<::Type>, 457 | { 458 | FieldBuilder { 459 | name: self.name, 460 | ty: Some(ty.into()), 461 | type_name: self.type_name, 462 | docs: self.docs, 463 | marker: PhantomData, 464 | } 465 | } 466 | } 467 | 468 | impl FieldBuilder { 469 | /// Initialize the type name of a field (optional). 470 | pub fn type_name(self, type_name: F::String) -> FieldBuilder { 471 | FieldBuilder { 472 | name: self.name, 473 | ty: self.ty, 474 | type_name: Some(type_name), 475 | docs: self.docs, 476 | marker: PhantomData, 477 | } 478 | } 479 | } 480 | 481 | impl FieldBuilder { 482 | #[cfg(feature = "docs")] 483 | /// Initialize the documentation of a field (for types in portable form, optional). 484 | pub fn docs_portable(mut self, docs: I) -> Self 485 | where 486 | I: IntoIterator::String>, 487 | { 488 | self.docs = docs.into_iter().collect(); 489 | self 490 | } 491 | } 492 | 493 | impl FieldBuilder { 494 | #[cfg(feature = "docs")] 495 | /// Initialize the documentation of a field (optional). 496 | pub fn docs(self, docs: &'static [&'static str]) -> Self { 497 | FieldBuilder { 498 | name: self.name, 499 | ty: self.ty, 500 | type_name: self.type_name, 501 | docs: docs.to_vec(), 502 | marker: PhantomData, 503 | } 504 | } 505 | 506 | #[cfg(not(feature = "docs"))] 507 | #[inline] 508 | /// Doc capture is not enabled via the "docs" feature so this is a no-op. 509 | pub fn docs(self, _docs: &'static [&'static str]) -> Self { 510 | self 511 | } 512 | 513 | /// Initialize the documentation of a field, always captured even if the "docs" feature is not 514 | /// enabled. 515 | pub fn docs_always(self, docs: &'static [&'static str]) -> Self { 516 | FieldBuilder { 517 | name: self.name, 518 | ty: self.ty, 519 | type_name: self.type_name, 520 | docs: docs.to_vec(), 521 | marker: PhantomData, 522 | } 523 | } 524 | } 525 | 526 | impl FieldBuilder { 527 | /// Complete building and return a new [`Field`]. 528 | pub fn finalize(self) -> Field { 529 | Field::new( 530 | self.name, 531 | self.ty.expect("Type should be set by builder"), 532 | self.type_name, 533 | self.docs, 534 | ) 535 | } 536 | } 537 | 538 | /// Builds a definition of a variant type i.e an `enum` 539 | #[derive(Default)] 540 | #[must_use] 541 | pub struct Variants { 542 | variants: Vec>, 543 | } 544 | 545 | impl Variants { 546 | /// Create a new [`VariantsBuilder`]. 547 | pub fn new() -> Self { 548 | Self { 549 | variants: Vec::new(), 550 | } 551 | } 552 | 553 | /// Add a variant 554 | pub fn variant(mut self, name: F::String, builder: B) -> Self 555 | where 556 | B: Fn(VariantBuilder) -> VariantBuilder, 557 | { 558 | let builder = builder(VariantBuilder::new(name)); 559 | self.variants.push(builder.finalize()); 560 | self 561 | } 562 | 563 | /// Add a unit variant (without fields). 564 | pub fn variant_unit(mut self, name: F::String, index: u8) -> Self { 565 | let builder = VariantBuilder::new(name).index(index); 566 | self.variants.push(builder.finalize()); 567 | self 568 | } 569 | 570 | /// Construct a new [`TypeDefVariant`] from the initialized builder variants. 571 | pub fn finalize(self) -> TypeDefVariant { 572 | TypeDefVariant::new(self.variants) 573 | } 574 | } 575 | 576 | /// State types for the `VariantBuilder` which requires an index. 577 | pub mod variant_state { 578 | /// State where the builder has not assigned an index to a variant. 579 | pub enum IndexNotAssigned {} 580 | /// State where the builder has assigned an index to a variant. 581 | pub enum IndexAssigned {} 582 | } 583 | 584 | /// Build a [`Variant`]. 585 | #[must_use] 586 | pub struct VariantBuilder { 587 | name: F::String, 588 | index: Option, 589 | fields: Vec>, 590 | discriminant: Option, 591 | docs: Vec, 592 | marker: PhantomData, 593 | } 594 | 595 | impl VariantBuilder { 596 | /// Create a new [`VariantBuilder`]. 597 | pub fn new(name: F::String) -> Self { 598 | Self { 599 | name, 600 | fields: Vec::new(), 601 | discriminant: None, 602 | index: None, 603 | docs: Vec::new(), 604 | marker: Default::default(), 605 | } 606 | } 607 | 608 | /// Set the variant's codec index. 609 | pub fn index(self, index: u8) -> VariantBuilder { 610 | VariantBuilder { 611 | name: self.name, 612 | index: Some(index), 613 | fields: self.fields, 614 | discriminant: self.discriminant, 615 | docs: self.docs, 616 | marker: Default::default(), 617 | } 618 | } 619 | } 620 | 621 | impl VariantBuilder { 622 | /// Set the variant's discriminant. 623 | pub fn discriminant(mut self, discriminant: u64) -> Self { 624 | self.discriminant = Some(discriminant); 625 | self 626 | } 627 | 628 | /// Initialize the variant's fields. 629 | pub fn fields(mut self, fields_builder: FieldsBuilder) -> Self { 630 | self.fields = fields_builder.finalize(); 631 | self 632 | } 633 | } 634 | 635 | impl VariantBuilder { 636 | #[cfg(feature = "docs")] 637 | /// Initialize the variant's documentation (for types in portable form). 638 | pub fn docs_portable(mut self, docs: I) -> Self 639 | where 640 | I: IntoIterator::String>, 641 | { 642 | self.docs = docs.into_iter().collect(); 643 | self 644 | } 645 | } 646 | 647 | impl VariantBuilder { 648 | #[cfg(feature = "docs")] 649 | /// Initialize the variant's documentation. 650 | pub fn docs(mut self, docs: &[&'static str]) -> Self { 651 | self.docs = docs.to_vec(); 652 | self 653 | } 654 | 655 | #[cfg(not(feature = "docs"))] 656 | #[inline] 657 | /// Doc capture is not enabled via the "docs" feature so this is a no-op. 658 | pub fn docs(self, _docs: &[&'static str]) -> Self { 659 | self 660 | } 661 | 662 | /// Initialize the variant's documentation, always captured even if the "docs" feature is not 663 | /// enabled. 664 | pub fn docs_always(mut self, docs: &[&'static str]) -> Self { 665 | self.docs = docs.to_vec(); 666 | self 667 | } 668 | } 669 | 670 | impl VariantBuilder { 671 | /// Complete building and create final [`Variant`] instance. 672 | pub fn finalize(self) -> Variant { 673 | Variant::new( 674 | self.name, 675 | self.fields, 676 | self.index.expect("Index should be assigned by the builder"), 677 | self.docs, 678 | ) 679 | } 680 | } 681 | -------------------------------------------------------------------------------- /src/form.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Provides form definitions. 16 | //! 17 | //! The forms provided here are used to generically communicate the mode a type 18 | //! identifier, type definition or structure is using. 19 | //! 20 | //! The default form is the `MetaForm`. 21 | //! It uses `MetaType` for communicating type identifiers and thus acts as 22 | //! a bridge from runtime to compile time type information. 23 | //! 24 | //! The `PortableForm` is a space-efficient representation 25 | //! that no longer has any connections to the interning registry and thus 26 | //! can no longer be used to retrieve information from the 27 | //! original registry. Its sole purpose is for space-efficient serialization. 28 | //! 29 | //! Other forms, such as a portable form that is still bound to the registry 30 | //! (also via lifetime tracking) are possible but current not needed. 31 | 32 | use crate::prelude::{any::TypeId, fmt::Debug}; 33 | 34 | use crate::{interner::UntrackedSymbol, meta_type::MetaType}; 35 | 36 | #[cfg(feature = "schema")] 37 | use schemars::JsonSchema; 38 | #[cfg(feature = "serde")] 39 | use serde::Serialize; 40 | 41 | /// Trait to support derivation of `JsonSchema` for schema generation. 42 | #[cfg(feature = "schema")] 43 | pub trait JsonSchemaMaybe: JsonSchema {} 44 | /// Trait to support derivation of `JsonSchema` for schema generation. 45 | #[cfg(not(feature = "schema"))] 46 | pub trait JsonSchemaMaybe {} 47 | 48 | /// Trait to control the internal structures of type definitions. 49 | /// 50 | /// This allows for type-level separation between free forms that can be 51 | /// instantiated out of the flux and portable forms that require some sort of 52 | /// interning data structures. 53 | pub trait Form { 54 | /// The type representing the type. 55 | type Type: PartialEq + Eq + PartialOrd + Ord + Clone + Debug + JsonSchemaMaybe; 56 | /// The string type. 57 | type String: AsRef + PartialEq + Eq + PartialOrd + Ord + Clone + Debug + JsonSchemaMaybe; 58 | } 59 | 60 | /// A meta meta-type. 61 | /// 62 | /// Allows to be converted into other forms such as portable form 63 | /// through the registry and `IntoPortable`. 64 | #[cfg_attr(feature = "schema", derive(JsonSchema))] 65 | #[cfg_attr(feature = "serde", derive(Serialize))] 66 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] 67 | pub enum MetaForm {} 68 | 69 | impl Form for MetaForm { 70 | type Type = MetaType; 71 | type String = &'static str; 72 | } 73 | 74 | /// Portable form that has its lifetime untracked in association to its interner. 75 | /// 76 | /// # Note 77 | /// 78 | /// This resolves some lifetime issues with self-referential structs (such as 79 | /// the registry itself) but can no longer be used to resolve to the original 80 | /// underlying data. 81 | #[cfg_attr(feature = "schema", derive(JsonSchema))] 82 | #[cfg_attr(feature = "serde", derive(Serialize))] 83 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] 84 | pub enum PortableForm {} 85 | 86 | cfg_if::cfg_if! { 87 | if #[cfg(any(feature = "std", feature = "decode"))] { 88 | impl Form for PortableForm { 89 | type Type = UntrackedSymbol; 90 | // Owned string required for decoding/deserialization 91 | type String = crate::prelude::string::String; 92 | } 93 | } else { 94 | impl Form for PortableForm { 95 | type Type = UntrackedSymbol; 96 | type String = &'static str; 97 | } 98 | } 99 | } 100 | 101 | // Blanket implementations 102 | #[cfg(not(feature = "schema"))] 103 | impl JsonSchemaMaybe for T {} 104 | #[cfg(feature = "schema")] 105 | impl JsonSchemaMaybe for T where T: JsonSchema {} 106 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::prelude::{ 16 | borrow::{Cow, ToOwned}, 17 | boxed::Box, 18 | collections::{BTreeMap, BTreeSet, BinaryHeap, VecDeque}, 19 | fmt, 20 | marker::PhantomData, 21 | ops::{Range, RangeInclusive}, 22 | rc::Rc, 23 | string::String, 24 | sync::Arc, 25 | time::Duration, 26 | vec::Vec, 27 | }; 28 | 29 | use crate::{ 30 | build::*, MetaType, Path, Type, TypeDefArray, TypeDefCompact, TypeDefPrimitive, 31 | TypeDefSequence, TypeDefTuple, TypeInfo, 32 | }; 33 | use core::num::{ 34 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, 35 | NonZeroU32, NonZeroU64, NonZeroU8, 36 | }; 37 | 38 | macro_rules! impl_metadata_for_primitives { 39 | ( $( $t:ty => $ident_kind:expr, )* ) => { $( 40 | impl TypeInfo for $t { 41 | type Identity = Self; 42 | 43 | fn type_info() -> Type { 44 | $ident_kind.into() 45 | } 46 | } 47 | )* } 48 | } 49 | 50 | impl_metadata_for_primitives!( 51 | bool => TypeDefPrimitive::Bool, 52 | char => TypeDefPrimitive::Char, 53 | u8 => TypeDefPrimitive::U8, 54 | u16 => TypeDefPrimitive::U16, 55 | u32 => TypeDefPrimitive::U32, 56 | u64 => TypeDefPrimitive::U64, 57 | u128 => TypeDefPrimitive::U128, 58 | i8 => TypeDefPrimitive::I8, 59 | i16 => TypeDefPrimitive::I16, 60 | i32 => TypeDefPrimitive::I32, 61 | i64 => TypeDefPrimitive::I64, 62 | i128 => TypeDefPrimitive::I128, 63 | ); 64 | 65 | impl TypeInfo for [T; N] { 66 | type Identity = Self; 67 | 68 | fn type_info() -> Type { 69 | TypeDefArray::new(N as u32, MetaType::new::()).into() 70 | } 71 | } 72 | 73 | macro_rules! impl_metadata_for_tuple { 74 | ( $($ty:ident),* ) => { 75 | impl<$($ty),*> TypeInfo for ($($ty,)*) 76 | where 77 | $( 78 | $ty: TypeInfo+ 'static, 79 | )* 80 | { 81 | type Identity = Self; 82 | 83 | fn type_info() -> Type { 84 | TypeDefTuple::new(tuple_meta_type!($($ty),*)).into() 85 | } 86 | } 87 | } 88 | } 89 | 90 | impl_metadata_for_tuple!(); 91 | impl_metadata_for_tuple!(A); 92 | impl_metadata_for_tuple!(A, B); 93 | impl_metadata_for_tuple!(A, B, C); 94 | impl_metadata_for_tuple!(A, B, C, D); 95 | impl_metadata_for_tuple!(A, B, C, D, E); 96 | impl_metadata_for_tuple!(A, B, C, D, E, F); 97 | impl_metadata_for_tuple!(A, B, C, D, E, F, G); 98 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H); 99 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I); 100 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J); 101 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K); 102 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); 103 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); 104 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); 105 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); 106 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); 107 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); 108 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); 109 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); 110 | impl_metadata_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); 111 | 112 | macro_rules! impl_for_non_zero { 113 | ( $( $t: ty: $inner: ty ),* $(,)? ) => { 114 | $( 115 | impl TypeInfo for $t { 116 | type Identity = Self; 117 | fn type_info() -> Type { 118 | Type::builder() 119 | .path(Path::prelude(stringify!($t))) 120 | .composite(Fields::unnamed().field(|f| f.ty::<$inner>())) 121 | } 122 | } 123 | )* 124 | }; 125 | } 126 | 127 | impl_for_non_zero!( 128 | NonZeroI8: i8, 129 | NonZeroI16: i16, 130 | NonZeroI32: i32, 131 | NonZeroI64: i64, 132 | NonZeroI128: i128, 133 | NonZeroU8: u8, 134 | NonZeroU16: u16, 135 | NonZeroU32: u32, 136 | NonZeroU64: u64, 137 | NonZeroU128: u128 138 | ); 139 | 140 | impl TypeInfo for Duration { 141 | type Identity = Self; 142 | 143 | fn type_info() -> Type { 144 | Type::builder().path(Path::prelude("Duration")).composite( 145 | Fields::unnamed() 146 | .field(|f| { 147 | // Seconds 148 | f.ty::().type_name("u64") 149 | }) 150 | .field(|f| { 151 | // Nanoseconds 152 | f.ty::().type_name("u32") 153 | }), 154 | ) 155 | } 156 | } 157 | 158 | impl TypeInfo for Vec 159 | where 160 | T: TypeInfo + 'static, 161 | { 162 | type Identity = [T]; 163 | 164 | fn type_info() -> Type { 165 | Self::Identity::type_info() 166 | } 167 | } 168 | 169 | impl TypeInfo for VecDeque 170 | where 171 | T: TypeInfo + 'static, 172 | { 173 | type Identity = [T]; 174 | 175 | fn type_info() -> Type { 176 | Self::Identity::type_info() 177 | } 178 | } 179 | 180 | impl TypeInfo for Option 181 | where 182 | T: TypeInfo + 'static, 183 | { 184 | type Identity = Self; 185 | 186 | fn type_info() -> Type { 187 | Type::builder() 188 | .path(Path::prelude("Option")) 189 | .type_params(type_params![T]) 190 | .variant( 191 | Variants::new() 192 | .variant("None", |v| v.index(0)) 193 | .variant("Some", |v| { 194 | v.index(1).fields(Fields::unnamed().field(|f| f.ty::())) 195 | }), 196 | ) 197 | } 198 | } 199 | 200 | impl TypeInfo for Result 201 | where 202 | T: TypeInfo + 'static, 203 | E: TypeInfo + 'static, 204 | { 205 | type Identity = Self; 206 | 207 | fn type_info() -> Type { 208 | Type::builder() 209 | .path(Path::prelude("Result")) 210 | .type_params(type_params!(T, E)) 211 | .variant( 212 | Variants::new() 213 | .variant("Ok", |v| { 214 | v.index(0).fields(Fields::unnamed().field(|f| f.ty::())) 215 | }) 216 | .variant("Err", |v| { 217 | v.index(1).fields(Fields::unnamed().field(|f| f.ty::())) 218 | }), 219 | ) 220 | } 221 | } 222 | 223 | impl TypeInfo for Cow<'static, T> 224 | where 225 | T: ToOwned + TypeInfo + ?Sized + 'static, 226 | { 227 | type Identity = Self; 228 | 229 | fn type_info() -> Type { 230 | Type::builder() 231 | .path(Path::prelude("Cow")) 232 | .type_params(type_params!(T)) 233 | .composite(Fields::unnamed().field(|f| f.ty::())) 234 | } 235 | } 236 | 237 | impl TypeInfo for BTreeMap 238 | where 239 | K: TypeInfo + 'static, 240 | V: TypeInfo + 'static, 241 | { 242 | type Identity = Self; 243 | 244 | fn type_info() -> Type { 245 | Type::builder() 246 | .path(Path::prelude("BTreeMap")) 247 | .type_params(type_params![K, V]) 248 | .composite(Fields::unnamed().field(|f| f.ty::<[(K, V)]>())) 249 | } 250 | } 251 | 252 | impl TypeInfo for BTreeSet 253 | where 254 | T: TypeInfo + 'static, 255 | { 256 | type Identity = Self; 257 | 258 | fn type_info() -> Type { 259 | Type::builder() 260 | .path(Path::prelude("BTreeSet")) 261 | .type_params(type_params![T]) 262 | .composite(Fields::unnamed().field(|f| f.ty::<[T]>())) 263 | } 264 | } 265 | 266 | impl TypeInfo for BinaryHeap 267 | where 268 | T: TypeInfo + 'static, 269 | { 270 | type Identity = Self; 271 | 272 | fn type_info() -> Type { 273 | Type::builder() 274 | .path(Path::prelude("BinaryHeap")) 275 | .type_params(type_params![T]) 276 | .composite(Fields::unnamed().field(|f| f.ty::<[T]>())) 277 | } 278 | } 279 | 280 | impl TypeInfo for Box 281 | where 282 | T: TypeInfo + ?Sized + 'static, 283 | { 284 | type Identity = T; 285 | 286 | fn type_info() -> Type { 287 | Self::Identity::type_info() 288 | } 289 | } 290 | 291 | impl TypeInfo for Rc 292 | where 293 | T: TypeInfo + ?Sized + 'static, 294 | { 295 | type Identity = T; 296 | 297 | fn type_info() -> Type { 298 | Self::Identity::type_info() 299 | } 300 | } 301 | 302 | impl TypeInfo for Arc 303 | where 304 | T: TypeInfo + ?Sized + 'static, 305 | { 306 | type Identity = T; 307 | 308 | fn type_info() -> Type { 309 | Self::Identity::type_info() 310 | } 311 | } 312 | 313 | impl TypeInfo for &T 314 | where 315 | T: TypeInfo + ?Sized + 'static, 316 | { 317 | type Identity = T; 318 | 319 | fn type_info() -> Type { 320 | Self::Identity::type_info() 321 | } 322 | } 323 | 324 | impl TypeInfo for &mut T 325 | where 326 | T: TypeInfo + ?Sized + 'static, 327 | { 328 | type Identity = T; 329 | 330 | fn type_info() -> Type { 331 | Self::Identity::type_info() 332 | } 333 | } 334 | 335 | impl TypeInfo for [T] 336 | where 337 | T: TypeInfo + 'static, 338 | { 339 | type Identity = Self; 340 | 341 | fn type_info() -> Type { 342 | TypeDefSequence::of::().into() 343 | } 344 | } 345 | 346 | impl TypeInfo for str { 347 | type Identity = Self; 348 | 349 | fn type_info() -> Type { 350 | TypeDefPrimitive::Str.into() 351 | } 352 | } 353 | 354 | impl TypeInfo for String { 355 | type Identity = str; 356 | 357 | fn type_info() -> Type { 358 | Self::Identity::type_info() 359 | } 360 | } 361 | 362 | pub(crate) type PhantomIdentity = PhantomData<()>; 363 | 364 | impl TypeInfo for PhantomData { 365 | type Identity = PhantomIdentity; 366 | 367 | fn type_info() -> Type { 368 | // Fields of this type should be filtered out and never appear in the type graph. 369 | Type::builder() 370 | .path(Path::prelude("PhantomData")) 371 | .docs(&["PhantomData placeholder, this type should be filtered out"]) 372 | .composite(Fields::unit()) 373 | } 374 | } 375 | 376 | impl TypeInfo for scale::Compact 377 | where 378 | T: TypeInfo + 'static, 379 | { 380 | type Identity = Self; 381 | fn type_info() -> Type { 382 | TypeDefCompact::new(MetaType::new::()).into() 383 | } 384 | } 385 | 386 | impl TypeInfo for Range 387 | where 388 | Idx: TypeInfo + 'static + PartialOrd + fmt::Debug, 389 | { 390 | type Identity = Self; 391 | fn type_info() -> Type { 392 | Type::builder() 393 | .path(Path::prelude("Range")) 394 | .type_params(type_params![Idx]) 395 | .composite( 396 | Fields::named() 397 | .field(|f| f.name("start").ty::().type_name("Idx")) 398 | .field(|f| f.name("end").ty::().type_name("Idx")), 399 | ) 400 | } 401 | } 402 | 403 | impl TypeInfo for RangeInclusive 404 | where 405 | Idx: TypeInfo + 'static + PartialOrd + fmt::Debug, 406 | { 407 | type Identity = Self; 408 | fn type_info() -> Type { 409 | Type::builder() 410 | .path(Path::prelude("RangeInclusive")) 411 | .type_params(type_params![Idx]) 412 | .composite( 413 | Fields::named() 414 | .field(|f| f.name("start").ty::().type_name("Idx")) 415 | .field(|f| f.name("end").ty::().type_name("Idx")), 416 | ) 417 | } 418 | } 419 | 420 | #[cfg(feature = "bit-vec")] 421 | mod bit_vec { 422 | use super::*; 423 | 424 | impl TypeInfo for bitvec::vec::BitVec 425 | where 426 | T: bitvec::store::BitStore + TypeInfo + 'static, 427 | O: bitvec::order::BitOrder + TypeInfo + 'static, 428 | { 429 | type Identity = Self; 430 | 431 | fn type_info() -> Type { 432 | crate::TypeDefBitSequence::new::().into() 433 | } 434 | } 435 | 436 | impl TypeInfo for bitvec::order::Lsb0 { 437 | type Identity = Self; 438 | 439 | fn type_info() -> Type { 440 | Type::builder() 441 | .path(Path::new("Lsb0", "bitvec::order")) 442 | .composite(Fields::unit()) 443 | } 444 | } 445 | 446 | impl TypeInfo for bitvec::order::Msb0 { 447 | type Identity = Self; 448 | 449 | fn type_info() -> Type { 450 | Type::builder() 451 | .path(Path::new("Msb0", "bitvec::order")) 452 | .composite(Fields::unit()) 453 | } 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/interner.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Interning data structure and associated symbol definitions. 16 | //! 17 | //! The interner is used by the registry in order to deduplicate strings and type 18 | //! definitions. Strings are uniquely identified by their contents while types 19 | //! are uniquely identified by their respective type identifiers. 20 | //! 21 | //! The interners provide a strict ordered sequence of cached (interned) 22 | //! elements and is later used for space-efficient serialization within the 23 | //! registry. 24 | 25 | use crate::prelude::{ 26 | collections::btree_map::{BTreeMap, Entry}, 27 | marker::PhantomData, 28 | vec::Vec, 29 | }; 30 | 31 | #[cfg(feature = "serde")] 32 | use serde::{Deserialize, Serialize}; 33 | 34 | #[cfg(feature = "schema")] 35 | use schemars::JsonSchema; 36 | 37 | /// A symbol that is not lifetime tracked. 38 | /// 39 | /// This can be used by self-referential types but 40 | /// can no longer be used to resolve instances. 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, scale::Encode, scale::Decode)] 42 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 43 | #[cfg_attr(feature = "serde", serde(transparent))] 44 | pub struct UntrackedSymbol { 45 | /// The index to the symbol in the interner table. 46 | #[codec(compact)] 47 | pub id: u32, 48 | #[cfg_attr(feature = "serde", serde(skip))] 49 | marker: PhantomData T>, 50 | } 51 | 52 | impl UntrackedSymbol { 53 | /// Returns the index to the symbol in the interner table. 54 | #[deprecated( 55 | since = "2.5.0", 56 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 57 | )] 58 | pub fn id(&self) -> u32 { 59 | self.id 60 | } 61 | } 62 | 63 | impl From for UntrackedSymbol { 64 | fn from(id: u32) -> Self { 65 | Self { 66 | id, 67 | marker: Default::default(), 68 | } 69 | } 70 | } 71 | 72 | #[cfg(feature = "schema")] 73 | impl JsonSchema for UntrackedSymbol { 74 | fn schema_name() -> String { 75 | String::from("UntrackedSymbol") 76 | } 77 | 78 | fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { 79 | gen.subschema_for::() 80 | } 81 | } 82 | 83 | /// A symbol from an interner. 84 | /// 85 | /// Can be used to resolve to the associated instance. 86 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 87 | #[cfg_attr(feature = "serde", derive(Serialize))] 88 | #[cfg_attr(feature = "serde", serde(transparent))] 89 | #[cfg_attr(feature = "schema", derive(JsonSchema))] 90 | pub struct Symbol<'a, T: 'a> { 91 | id: u32, 92 | #[cfg_attr(feature = "serde", serde(skip))] 93 | marker: PhantomData &'a T>, 94 | } 95 | 96 | impl Symbol<'_, T> { 97 | /// Removes the lifetime tracking for this symbol. 98 | /// 99 | /// # Note 100 | /// 101 | /// - This can be useful in situations where a data structure owns all 102 | /// symbols and interners and can verify accesses by itself. 103 | /// - For further safety reasons an untracked symbol can no longer be used 104 | /// to resolve from an interner. It is still useful for serialization 105 | /// purposes. 106 | /// 107 | /// # Safety 108 | /// 109 | /// Although removing lifetime constraints this operation can be 110 | /// considered to be safe since untracked symbols can no longer be 111 | /// used to resolve their associated instance from the interner. 112 | pub fn into_untracked(self) -> UntrackedSymbol { 113 | UntrackedSymbol { 114 | id: self.id, 115 | marker: PhantomData, 116 | } 117 | } 118 | } 119 | 120 | /// Interning data structure generic over the element type. 121 | /// 122 | /// For the sake of simplicity and correctness we are using a rather naive 123 | /// implementation. 124 | /// 125 | /// # Usage 126 | /// 127 | /// This is used in order to quite efficiently cache strings and type 128 | /// definitions uniquely identified by their associated type identifiers. 129 | #[derive(Debug, PartialEq, Eq)] 130 | #[cfg_attr(feature = "serde", derive(Serialize))] 131 | #[cfg_attr(feature = "serde", serde(transparent))] 132 | #[cfg_attr(feature = "schema", derive(JsonSchema))] 133 | pub struct Interner { 134 | /// A mapping from the interned elements to their respective space-efficient 135 | /// identifiers. 136 | /// 137 | /// The idenfitiers can be used to retrieve information about the original 138 | /// element from the interner. 139 | #[cfg_attr(feature = "serde", serde(skip))] 140 | map: BTreeMap, 141 | /// The ordered sequence of cached elements. 142 | /// 143 | /// This is used to efficiently provide access to the cached elements and 144 | /// to establish a strict ordering upon them since each is uniquely 145 | /// identified later by its position in the vector. 146 | vec: Vec, 147 | } 148 | 149 | impl Interner 150 | where 151 | T: Ord, 152 | { 153 | /// Creates a new empty interner. 154 | pub fn new() -> Self { 155 | Self { 156 | map: BTreeMap::new(), 157 | vec: Vec::new(), 158 | } 159 | } 160 | } 161 | 162 | impl Default for Interner { 163 | fn default() -> Self { 164 | Self::new() 165 | } 166 | } 167 | 168 | impl Interner 169 | where 170 | T: Ord + Clone, 171 | { 172 | /// Interns the given element or returns its associated symbol if it has 173 | /// already been interned. 174 | pub fn intern_or_get(&mut self, s: T) -> (bool, Symbol) { 175 | let next_id = self.vec.len(); 176 | let (inserted, sym_id) = match self.map.entry(s.clone()) { 177 | Entry::Vacant(vacant) => { 178 | vacant.insert(next_id); 179 | self.vec.push(s); 180 | (true, next_id) 181 | } 182 | Entry::Occupied(occupied) => (false, *occupied.get()), 183 | }; 184 | ( 185 | inserted, 186 | Symbol { 187 | id: sym_id as u32, 188 | marker: PhantomData, 189 | }, 190 | ) 191 | } 192 | 193 | /// Returns the symbol of the given element or `None` if it hasn't been 194 | /// interned already. 195 | pub fn get(&self, sym: &T) -> Option> { 196 | self.map.get(sym).map(|&id| Symbol { 197 | id: id as u32, 198 | marker: PhantomData, 199 | }) 200 | } 201 | 202 | /// Resolves the original element given its associated symbol or 203 | /// returns `None` if it has not been interned yet. 204 | pub fn resolve(&self, sym: Symbol) -> Option<&T> { 205 | let idx = sym.id as usize; 206 | if idx >= self.vec.len() { 207 | return None; 208 | } 209 | self.vec.get(idx) 210 | } 211 | 212 | /// Returns the ordered sequence of interned elements. 213 | pub fn elements(&self) -> &[T] { 214 | &self.vec 215 | } 216 | } 217 | 218 | #[cfg(test)] 219 | mod tests { 220 | use super::*; 221 | 222 | type StringInterner = Interner<&'static str>; 223 | 224 | fn assert_id(interner: &mut StringInterner, new_symbol: &'static str, expected_id: u32) { 225 | let actual_id = interner.intern_or_get(new_symbol).1.id; 226 | assert_eq!(actual_id, expected_id,); 227 | } 228 | 229 | fn assert_resolve(interner: &StringInterner, symbol_id: u32, expected_str: E) 230 | where 231 | E: Into>, 232 | { 233 | let actual_str = interner.resolve(Symbol { 234 | id: symbol_id, 235 | marker: PhantomData, 236 | }); 237 | assert_eq!(actual_str.cloned(), expected_str.into(),); 238 | } 239 | 240 | #[test] 241 | fn simple() { 242 | let mut interner = StringInterner::new(); 243 | assert_id(&mut interner, "Hello", 0); 244 | assert_id(&mut interner, ", World!", 1); 245 | assert_id(&mut interner, "1 2 3", 2); 246 | assert_id(&mut interner, "Hello", 0); 247 | 248 | assert_resolve(&interner, 0, "Hello"); 249 | assert_resolve(&interner, 1, ", World!"); 250 | assert_resolve(&interner, 2, "1 2 3"); 251 | assert_resolve(&interner, 3, None); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![cfg_attr(not(feature = "std"), no_std)] 16 | #![deny(missing_docs)] 17 | 18 | //! Efficient and space-efficient serialization of Rust types. 19 | //! 20 | //! This library provides structures to easily retrieve compile-time type 21 | //! information at runtime and also to serialize this information in a 22 | //! space-efficient form, aka `PortableForm`. 23 | //! 24 | //! # Registry 25 | //! 26 | //! At the heart of its functionality is the [`Registry`](`crate::Registry`) 27 | //! that acts as a cache for known types in order to efficiently deduplicate 28 | //! types and ensure a space-efficient serialization. 29 | //! 30 | //! # Type Information 31 | //! 32 | //! Information about types is provided via the [`TypeInfo`](`crate::TypeInfo`) 33 | //! trait. 34 | //! 35 | //! This trait should be implemented for all types that are serializable. 36 | //! `scale-info` provides implementations for all commonly used Rust standard 37 | //! types and a derive macro for implementing of custom types. 38 | //! 39 | //! ## Deriving `TypeInfo` 40 | //! 41 | //! Enable the `derive` feature of this crate: 42 | //! 43 | //! ```toml 44 | //! scale-info = { version = "2.0.0", features = ["derive"] } 45 | //! ``` 46 | //! 47 | //! ```ignore 48 | //! use scale_info::TypeInfo; 49 | //! 50 | //! #[derive(TypeInfo)] 51 | //! struct MyStruct { 52 | //! a: u32, 53 | //! b: MyEnum, 54 | //! } 55 | //! 56 | //! #[derive(TypeInfo)] 57 | //! enum MyEnum { 58 | //! A(bool), 59 | //! B { f: Vec }, 60 | //! C, 61 | //! } 62 | //! ``` 63 | //! 64 | //! ### Attributes 65 | //! 66 | //! #### `#[scale_info(bounds(..))]` 67 | //! 68 | //! Replace the auto-generated `where` clause bounds for the derived `TypeInfo` implementation. 69 | //! 70 | //! ```ignore 71 | //! #[derive(TypeInfo)] 72 | //! #[scale_info(bounds(T: TypeInfo + 'static))] 73 | //! struct MyStruct { 74 | //! a: Vec, 75 | //! } 76 | //! ``` 77 | //! 78 | //! The derive macro automatically adds `TypeInfo` bounds for all type parameters, and all field 79 | //! types containing type parameters or associated types. 80 | //! 81 | //! This is naive and sometimes adds unnecessary bounds, since on a syntactical level there is no 82 | //! way to differentiate between a generic type constructor and a type alias with a generic argument 83 | //! e.g. 84 | //! 85 | //! ```ignore 86 | //! trait MyTrait { 87 | //! type A; 88 | //! } 89 | //! 90 | //! type MyAlias = ::A; 91 | //! 92 | //! #[derive(TypeInfo)] 93 | //! struct MyStruct { 94 | //! a: MyAlias, 95 | //! b: Vec, 96 | //! } 97 | //! ``` 98 | //! 99 | //! So for the above, since a `MyAlias: TypeInfo` bound is required, and we can't distinguish 100 | //! between `MyAlias` and `Vec`, then the `TypeInfo` bound is simply added for all 101 | //! fields which contain any type param. In this case the redundant `Vec: TypeInfo` 102 | //! would be added. 103 | //! 104 | //! This is usually okay, but in some circumstances can cause problems, for example with the 105 | //! [`overflow evaluating the requirement`] error [here](https://github.com/paritytech/scale-info/blob/master/test_suite/tests/ui/pass_custom_bounds_fix_overflow.rs). 106 | //! 107 | //! The `bounds` attribute provides an ["escape hatch"](https://serde.rs/attr-bound.html) to allow 108 | //! the programmer control of the `where` clause on the generated `impl`, to solve this and other 109 | //! issues that can't be foreseen by the auto-generated bounds heuristic. 110 | //! 111 | //! #### `#[scale_info(skip_type_params(..))]` 112 | //! 113 | //! Remove the requirement for the specified type params to implement `TypeInfo`. 114 | //! 115 | //! Consider a simple example of a type parameter which is used for associated types, but the type 116 | //! itself does not carry any type information. Consider this common pattern: 117 | //! 118 | //! ```ignore 119 | //! trait Config { 120 | //! type Balance; 121 | //! } 122 | //! 123 | //! struct Runtime; // doesn't implement `TypeInfo` 124 | //! 125 | //! impl Config for Runtime { 126 | //! type Balance = u64; 127 | //! } 128 | //! 129 | //! #[allow(unused)] 130 | //! #[derive(TypeInfo)] 131 | //! #[scale_info(skip_type_params(T))] 132 | //! struct A { 133 | //! balance: T::Balance, 134 | //! marker: core::marker::PhantomData, 135 | //! } 136 | //! 137 | //! fn assert_type_info() {} 138 | //! 139 | //! fn main() { 140 | //! // without the `skip_type_params` attribute this will fail. 141 | //! assert_type_info::>(); 142 | //! } 143 | //! ``` 144 | //! 145 | //! By default, the derived `TypeInfo` implementation will add the type of all type parameters to 146 | //! the `TypeParameter` specification e.g. 147 | //! 148 | //! `type_params(vec![TypeParameter::new("T", Some(MetaType::new::()))])` 149 | //! 150 | //! In the example above, this will cause a compiler error because `Runtime` is the concrete tyoe 151 | //! for `T`, which does not satisfy the `TypeInfo` requirement of `MetaType::new::()`. 152 | //! 153 | //! Simply adding a `TypeInfo` derive to `Runtime` is one way of solving this, but that could be 154 | //! misleading (why does it need `TypeInfo` if a value of that type is never encoded?), and can 155 | //! sometimes require adding `TypeInfo` bounds in other impl blocks. 156 | //! 157 | //! The `skip_type_params` attribute solves this, providing an additional "escape hatch" which 158 | //! prevents the given type parameter's type information being required: 159 | //! 160 | //! `type_params(vec![TypeParameter::new("T", None)])` 161 | //! 162 | //! The generated type params do not now require `T` to implement `TypeInfo`, so the auto-generated 163 | //! bound is not added to the generated `TypeInfo` `where` clause. 164 | //! 165 | //! #### Combining `bounds` and `skip_type_params` 166 | //! 167 | //! These two attributes can complement one another, particularly in the case where using `bounds` 168 | //! would still require manually adding a `TypeInfo` bound for the type parameter: 169 | //! 170 | //! ```ignore 171 | //! #[derive(TypeInfo)] 172 | //! #[scale_info(bounds(), skip_type_params(T))] 173 | //! struct A { 174 | //! marker: core::marker::PhantomData, 175 | //! } 176 | //! ``` 177 | //! 178 | //! Without `skip_type_params` in the example above, it would require the `TypeInfo` bounds for `T` 179 | //! to be added manually e.g. `#[scale_info(bounds(T: TypeInfo + 'static))]`. Since the intention of 180 | //! the empty bounds is to **remove** all type bounds, then the addition of `skip_type_params` 181 | //! allows this to compile successfully. 182 | //! 183 | //! ##### Precedence 184 | //! 185 | //! When used independently, both attributes modify the `where` clause of the derived `TypeInfo` 186 | //! impl. When used together, the predicates supplied in the `bounds` attribute replaces *all* 187 | //! auto-generated bounds, and `skip_type_params` will have no effect on the resulting `where` 188 | //! clause. 189 | //! 190 | //! **Note:** When using `bounds` without `skip_type_params`, it is therefore required to manually 191 | //! add a `TypeInfo` bound for any non skipped type parameters. The compiler will let you know. 192 | //! 193 | //! #### `#[scale_info(capture_docs = "default|always|never")]` 194 | //! 195 | //! Docs for types, fields and variants can all be captured by the `docs` feature being enabled. 196 | //! This can be overridden using the `capture_docs` attribute: 197 | //! 198 | //! `#[scale_info(capture_docs = "default")]` will capture docs iff the `docs` feature is enabled. 199 | //! This is the default if `capture_docs` is not specified. 200 | //! 201 | //! `#[scale_info(capture_docs = "always")]` will capture docs for the annotated type even if the 202 | //! `docs` feature is *not* enabled. 203 | //! 204 | //! `#[scale_info(capture_docs = "never")]` will *not* capture docs for the annotated type even if 205 | //! the `docs` is enabled. 206 | //! 207 | //! This is useful e.g. when compiling metadata into a Wasm blob, and it is desirable to keep the 208 | //! binary size as small as possible, so the `docs` feature would be disabled. In case the docs for 209 | //! some types is necessary they could be enabled on a per-type basis with the above attribute. 210 | //! 211 | //! #### `#[scale_info(crate = path::to::crate)]` 212 | //! 213 | //! Specify a path to the scale-info crate instance to use when referring to the APIs from generated 214 | //! code. This is normally only applicable when invoking re-exported scale-info derives from a public 215 | //! macro in a different crate. For example: 216 | //! ```ignore 217 | //! use scale_info_reexport::info::TypeInfo; 218 | //! 219 | //! #[derive(TypeInfo)] 220 | //! #[scale_info(crate = scale_info_reexport::info)] 221 | //! enum TestEnum { 222 | //! FirstVariant, 223 | //! SecondVariant, 224 | //! } 225 | //! ``` 226 | //! 227 | //! #### `#[scale_info(replace_segment("search", "replace"))]` 228 | //! 229 | //! Specify to rename a segment in the `path` returned by the [`TypeInfo::path`] function. 230 | //! Normally the path is generated by using the `module_path!` macro. This path includes 231 | //! the crate name and all the modules up to the declaration of the type. Sometimes it 232 | //! is useful to replace one of these segments to ensure that for example a renaming 233 | //! of the crate isn't propagated to downstream users. Be aware that if a `crate-name` 234 | //! contains an hypen, the actual segment is `crate_name`. The `replace` name needs 235 | //! to be a valid Rust identifier. The attribute is allowed to be passed multiple 236 | //! times to replace multiple segments. 237 | //! 238 | //! Example: 239 | //! ```ignore 240 | //! use scale_info_reexport::info::TypeInfo; 241 | //! 242 | //! #[derive(TypeInfo)] 243 | //! #[scale_info(replace_segment("something", "better_name"))] 244 | //! #[scale_info(replace_segment("TestEnum", "BetterEnumName"))] 245 | //! enum TestEnum { 246 | //! FirstVariant, 247 | //! SecondVariant, 248 | //! } 249 | //! ``` 250 | //! 251 | //! # Forms 252 | //! 253 | //! To bridge between compile-time type information and runtime the 254 | //! [`MetaForm`](`crate::form::MetaForm`) is used to easily retrieve all 255 | //! information needed to uniquely identify types. 256 | //! 257 | //! The `MetaForm` and its associated `Registry` can be transformed into the 258 | //! space-efficient form by the [`IntoPortable`](`crate::IntoPortable`) trait; it is 259 | //! used internally by the [`Registry`](`crate::Registry`) in order to convert 260 | //! the expanded types into their space-efficient form. 261 | //! 262 | //! # Symbols and Namespaces 263 | //! 264 | //! To differentiate two types sharing the same name, namespaces are used. 265 | //! Commonly the namespace is equal to the one where the type has been defined 266 | //! in. For Rust prelude types such as [`Option`](`std::option::Option`) and 267 | //! [`Result`](`std::result::Result`) the root namespace (empty namespace) is 268 | //! used. 269 | //! 270 | //! To use this library simply use the [`MetaForm`](`crate::form::MetaForm`) 271 | //! initially with your own data structures; make them generic over the 272 | //! [`Form`](`crate::form::Form`) trait just as has been done in this crate with 273 | //! [`TypeInfo`](`crate::TypeInfo`) in order to get a simple implementation of 274 | //! [`IntoPortable`](`crate::IntoPortable`). Use a single instance of the 275 | //! [`Registry`](`crate::Registry`) for compaction and provide this registry 276 | //! instance upon serialization. 277 | //! 278 | //! A usage example can be found in ink! here: 279 | //! https://github.com/paritytech/ink/blob/master/abi/src/specs.rs 280 | 281 | /// Takes a number of types and returns a vector that contains their respective 282 | /// [`MetaType`](`crate::MetaType`) instances. 283 | /// 284 | /// This is useful for places that require inputs of iterators over [`MetaType`](`crate::MetaType`) 285 | /// instances and provide a way out of code bloat in these scenarios. 286 | /// 287 | /// # Example 288 | /// 289 | /// ``` 290 | /// # use scale_info::tuple_meta_type; 291 | /// assert_eq!( 292 | /// tuple_meta_type!(i32, [u8; 32], String), 293 | /// { 294 | /// use scale_info::MetaType; 295 | /// let mut vec = Vec::new(); 296 | /// vec.push(MetaType::new::()); 297 | /// vec.push(MetaType::new::<[u8; 32]>()); 298 | /// vec.push(MetaType::new::()); 299 | /// vec 300 | /// } 301 | /// ); 302 | /// ``` 303 | #[macro_export] 304 | macro_rules! tuple_meta_type { 305 | ( $($ty:ty),* ) => { 306 | { 307 | $crate::prelude::vec![ 308 | $( 309 | $crate::MetaType::new::<$ty>(), 310 | )* 311 | ] 312 | } 313 | } 314 | } 315 | 316 | /// Construct a vector of `TypeParameter`s from pairs of the name and the concrete type. 317 | /// 318 | /// # Example 319 | /// 320 | /// ``` 321 | /// # use scale_info::{named_type_params, MetaType, TypeParameter}; 322 | /// assert_eq!( 323 | /// named_type_params![(T, u8), (U, u32)], 324 | /// vec! [ 325 | /// TypeParameter::new("T", Some(MetaType::new::())), 326 | /// TypeParameter::new("U", Some(MetaType::new::())), 327 | /// ] 328 | /// ); 329 | /// ``` 330 | #[macro_export] 331 | macro_rules! named_type_params { 332 | ( $(($tp:ty, $ty:ty)),* ) => { 333 | { 334 | $crate::prelude::vec![ 335 | $( 336 | $crate::TypeParameter::<$crate::form::MetaForm>::new( 337 | ::core::stringify!($tp), 338 | Some($crate::MetaType::new::<$ty>()) 339 | ), 340 | )* 341 | ] 342 | } 343 | } 344 | } 345 | 346 | /// Construct a vector of [`TypeParameter`] instances with the name of the type parameter, 347 | /// together with its concrete [`MetaType`]. 348 | #[macro_export] 349 | macro_rules! type_params { 350 | ( $($ty:ty),* ) => { 351 | $crate::named_type_params!{ $( ($ty, $ty) ),* } 352 | } 353 | } 354 | 355 | pub mod prelude; 356 | 357 | pub mod build; 358 | pub mod form; 359 | mod impls; 360 | pub mod interner; 361 | mod meta_type; 362 | mod portable; 363 | mod registry; 364 | mod ty; 365 | mod utils; 366 | 367 | #[doc(hidden)] 368 | pub use scale; 369 | 370 | pub use self::{ 371 | meta_type::MetaType, 372 | portable::{PortableRegistry, PortableRegistryBuilder, PortableType}, 373 | registry::{IntoPortable, Registry}, 374 | ty::*, 375 | }; 376 | 377 | #[cfg(feature = "derive")] 378 | pub use scale_info_derive::TypeInfo; 379 | 380 | /// Implementors return their meta type information. 381 | pub trait TypeInfo { 382 | /// The type identifying for which type info is provided. 383 | /// 384 | /// # Note 385 | /// 386 | /// This is used to uniquely identify a type via [`core::any::TypeId::of`]. In most cases it 387 | /// will just be `Self`, but can be used to unify different types which have the same encoded 388 | /// representation e.g. reference types `Box`, `&T` and `&mut T`. 389 | type Identity: ?Sized + 'static; 390 | 391 | /// Returns the static type identifier for `Self`. 392 | fn type_info() -> Type; 393 | } 394 | 395 | /// Convenience trait for implementors, combining `TypeInfo` and `'static` bounds. 396 | /// 397 | /// # Note 398 | /// 399 | /// Currently because of the `'static` constraint on [`std::any::TypeId::of`] (see [`MetaType`]), 400 | /// `TypeInfo` constraints must also be accompanied by a `'static` bound. This trait is useful to 401 | /// implementors so only a single constraint is required. 402 | pub trait StaticTypeInfo: TypeInfo + 'static {} 403 | 404 | impl StaticTypeInfo for T where T: TypeInfo + 'static {} 405 | 406 | /// Returns the runtime bridge to the types compile-time type information. 407 | pub fn meta_type() -> MetaType 408 | where 409 | T: ?Sized + TypeInfo + 'static, 410 | { 411 | MetaType::new::() 412 | } 413 | 414 | #[cfg(test)] 415 | mod tests; 416 | -------------------------------------------------------------------------------- /src/meta_type.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::prelude::{ 16 | any::TypeId, 17 | cmp::Ordering, 18 | fmt::{Debug, Error as FmtError, Formatter}, 19 | hash::{Hash, Hasher}, 20 | }; 21 | 22 | use crate::{Type, TypeInfo}; 23 | 24 | /// A metatype abstraction. 25 | /// 26 | /// Allows to store compile-time type information at runtime. 27 | /// This again allows to derive type ID and type definition from it. 28 | /// 29 | /// This needs a conversion to another representation of types 30 | /// in order to be serializable. 31 | #[derive(Clone, Copy)] 32 | pub struct MetaType { 33 | /// Function pointer to get type information. 34 | fn_type_info: fn() -> Type, 35 | // The standard type ID (ab)used in order to provide 36 | // cheap implementations of the standard traits 37 | // such as `PartialEq`, `PartialOrd`, `Debug` and `Hash`. 38 | type_id: TypeId, 39 | } 40 | 41 | impl PartialEq for MetaType { 42 | fn eq(&self, other: &Self) -> bool { 43 | self.type_id == other.type_id 44 | } 45 | } 46 | 47 | impl Eq for MetaType {} 48 | 49 | impl PartialOrd for MetaType { 50 | fn partial_cmp(&self, other: &Self) -> Option { 51 | Some(self.cmp(other)) 52 | } 53 | } 54 | 55 | impl Ord for MetaType { 56 | fn cmp(&self, other: &Self) -> Ordering { 57 | self.type_id.cmp(&other.type_id) 58 | } 59 | } 60 | 61 | impl Hash for MetaType { 62 | fn hash(&self, state: &mut H) 63 | where 64 | H: Hasher, 65 | { 66 | self.type_id.hash(state) 67 | } 68 | } 69 | 70 | impl Debug for MetaType { 71 | fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { 72 | self.type_id.fmt(f) 73 | } 74 | } 75 | 76 | impl MetaType { 77 | /// Creates a new meta type from the given compile-time known type. 78 | pub fn new() -> Self 79 | where 80 | T: TypeInfo + ?Sized + 'static, 81 | { 82 | Self { 83 | fn_type_info: ::type_info, 84 | type_id: TypeId::of::(), 85 | } 86 | } 87 | 88 | /// Returns the meta type information. 89 | pub fn type_info(&self) -> Type { 90 | (self.fn_type_info)() 91 | } 92 | 93 | /// Returns the type identifier provided by `core::any`. 94 | pub fn type_id(&self) -> TypeId { 95 | self.type_id 96 | } 97 | 98 | /// Returns true if this represents a type of [`core::marker::PhantomData`]. 99 | pub(crate) fn is_phantom(&self) -> bool { 100 | self == &MetaType::new::() 101 | } 102 | } 103 | 104 | #[cfg(feature = "schema")] 105 | impl schemars::JsonSchema for MetaType { 106 | fn schema_name() -> String { 107 | "MetaType".into() 108 | } 109 | 110 | fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { 111 | // since MetaType does not really get serialized, we don't care about its actual schema 112 | gen.subschema_for::() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Exports from `std`, `core` and `alloc` crates. 16 | //! 17 | //! Guarantees a stable interface between `std` and `no_std` modes. 18 | 19 | #[cfg(not(feature = "std"))] 20 | extern crate alloc; 21 | 22 | use cfg_if::cfg_if; 23 | 24 | cfg_if! { 25 | if #[cfg(feature = "std")] { 26 | pub use std::{ 27 | any, 28 | borrow, 29 | boxed, 30 | cmp, 31 | collections, 32 | fmt, 33 | format, 34 | hash, 35 | marker, 36 | mem, 37 | num, 38 | ops, 39 | string, 40 | sync, 41 | time, 42 | vec, 43 | rc, 44 | iter, 45 | }; 46 | } else { 47 | pub use alloc::{ 48 | borrow, 49 | boxed, 50 | collections, 51 | format, 52 | string, 53 | sync, 54 | vec, 55 | rc 56 | }; 57 | 58 | pub use core::{ 59 | any, 60 | cmp, 61 | fmt, 62 | hash, 63 | marker, 64 | mem, 65 | num, 66 | ops, 67 | time, 68 | iter, 69 | }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/registry.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! The registry stores type definitions in a space-efficient manner. 16 | //! 17 | //! This is done by deduplicating common types in order to reuse their 18 | //! definitions which otherwise can grow arbitrarily large. A type is uniquely 19 | //! identified by its type identifier that is therefore used to refer to types 20 | //! and their definitions. 21 | //! 22 | //! Types with the same name are uniquely identifiable by introducing 23 | //! namespaces. The normal Rust namespace of a type is used, except for the Rust 24 | //! prelude types that live in the so-called root namespace which is empty. 25 | 26 | use crate::{ 27 | form::Form, 28 | prelude::{any::TypeId, collections::BTreeMap, fmt::Debug, vec::Vec}, 29 | }; 30 | 31 | use crate::{ 32 | form::PortableForm, 33 | interner::{Interner, UntrackedSymbol}, 34 | meta_type::MetaType, 35 | Type, 36 | }; 37 | 38 | /// Convert the type definition into the portable form using a registry. 39 | pub trait IntoPortable { 40 | /// The portable version of `Self`. 41 | type Output; 42 | 43 | /// Convert `self` to the portable form by using the registry for caching. 44 | fn into_portable(self, registry: &mut Registry) -> Self::Output; 45 | } 46 | 47 | impl IntoPortable for &'static str { 48 | type Output = ::String; 49 | 50 | fn into_portable(self, _registry: &mut Registry) -> Self::Output { 51 | self.into() 52 | } 53 | } 54 | 55 | /// The registry for space-efficient storage of type identifiers and 56 | /// definitions. 57 | /// 58 | /// The registry consists of a cache for type identifiers and definitions. 59 | /// 60 | /// When adding a type to the registry, all of its sub-types are registered 61 | /// recursively as well. A type is considered a sub-type of another type if it 62 | /// is used by its identifier or structure. 63 | /// 64 | /// # Note 65 | /// 66 | /// A type can be a sub-type of itself. In this case the registry has a builtin 67 | /// mechanism to stop recursion and avoid going into an infinite loop. 68 | #[derive(Debug, PartialEq, Eq)] 69 | pub struct Registry { 70 | /// The cache for already registered types. 71 | /// 72 | /// This is just an accessor to the actual database 73 | /// for all types found in the `types` field. 74 | type_table: Interner, 75 | /// The database where registered types reside. 76 | /// 77 | /// The contents herein is used for serlialization. 78 | types: BTreeMap, Type>, 79 | } 80 | 81 | impl Default for Registry { 82 | fn default() -> Self { 83 | Self::new() 84 | } 85 | } 86 | 87 | impl Registry { 88 | /// Creates a new empty registry. 89 | pub fn new() -> Self { 90 | Self { 91 | type_table: Interner::new(), 92 | types: BTreeMap::new(), 93 | } 94 | } 95 | 96 | /// Registers the given type ID into the registry. 97 | /// 98 | /// Returns `false` as the first return value if the type ID has already 99 | /// been registered into this registry. 100 | /// Returns the associated type ID symbol as second return value. 101 | /// 102 | /// # Note 103 | /// 104 | /// This is an internal API and should not be called directly from the 105 | /// outside. 106 | fn intern_type_id(&mut self, type_id: TypeId) -> (bool, UntrackedSymbol) { 107 | let (inserted, symbol) = self.type_table.intern_or_get(type_id); 108 | (inserted, symbol.into_untracked()) 109 | } 110 | 111 | /// Registers the given type into the registry and returns 112 | /// its associated type ID symbol. 113 | /// 114 | /// # Note 115 | /// 116 | /// Due to safety requirements the returns type ID symbol cannot 117 | /// be used later to resolve back to the associated type definition. 118 | /// However, since this facility is going to be used for serialization 119 | /// purposes this functionality isn't needed anyway. 120 | pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol { 121 | let (inserted, symbol) = self.intern_type_id(ty.type_id()); 122 | if inserted { 123 | let portable_id = ty.type_info().into_portable(self); 124 | self.types.insert(symbol, portable_id); 125 | } 126 | symbol 127 | } 128 | 129 | /// Calls `register_type` for each `MetaType` in the given `iter`. 130 | pub fn register_types(&mut self, iter: I) -> Vec> 131 | where 132 | I: IntoIterator, 133 | { 134 | iter.into_iter() 135 | .map(|i| self.register_type(&i)) 136 | .collect::>() 137 | } 138 | 139 | /// Converts an iterator into a Vec of the equivalent portable 140 | /// representations. 141 | pub fn map_into_portable(&mut self, iter: I) -> Vec 142 | where 143 | I: IntoIterator, 144 | T: IntoPortable, 145 | { 146 | iter.into_iter() 147 | .map(|i| i.into_portable(self)) 148 | .collect::>() 149 | } 150 | 151 | /// Returns an iterator over the types with their keys 152 | pub fn types(&self) -> impl Iterator, &Type)> { 153 | self.types.iter() 154 | } 155 | } 156 | 157 | #[cfg(test)] 158 | mod tests { 159 | use super::*; 160 | use crate::{build::Fields, meta_type, Path, TypeDef, TypeInfo}; 161 | 162 | #[test] 163 | fn recursive_struct_with_references() { 164 | #[allow(unused)] 165 | struct RecursiveRefs<'a> { 166 | boxed: Box>, 167 | reference: &'a RecursiveRefs<'a>, 168 | mutable_reference: &'a mut RecursiveRefs<'a>, 169 | } 170 | 171 | impl TypeInfo for RecursiveRefs<'static> { 172 | type Identity = Self; 173 | 174 | fn type_info() -> Type { 175 | Type::builder() 176 | .path(Path::new("RecursiveRefs", module_path!())) 177 | .composite( 178 | Fields::named() 179 | .field(|f| { 180 | f.ty::>() 181 | .name("boxed") 182 | .type_name("Box < RecursiveRefs >") 183 | }) 184 | .field(|f| { 185 | f.ty::<&'static RecursiveRefs<'static>>() 186 | .name("reference") 187 | .type_name("&RecursiveRefs") 188 | }) 189 | .field(|f| { 190 | f.ty::<&'static mut RecursiveRefs<'static>>() 191 | .name("mutable_reference") 192 | .type_name("&mut RecursiveRefs") 193 | }), 194 | ) 195 | } 196 | } 197 | 198 | let mut registry = Registry::new(); 199 | let type_id = registry.register_type(&meta_type::()); 200 | 201 | let recursive = registry.types.get(&type_id).unwrap(); 202 | if let TypeDef::Composite(composite) = &recursive.type_def { 203 | for field in &composite.fields { 204 | assert_eq!(field.ty, type_id) 205 | } 206 | } else { 207 | panic!("Should be a composite type definition") 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | build::*, 17 | prelude::{ 18 | borrow::Cow, 19 | boxed::Box, 20 | collections::{BTreeMap, BTreeSet, BinaryHeap}, 21 | string::String, 22 | vec, 23 | }, 24 | *, 25 | }; 26 | use core::marker::PhantomData; 27 | use scale::Compact; 28 | use std::num::NonZeroU32; 29 | 30 | fn assert_type(expected: E) 31 | where 32 | T: TypeInfo + ?Sized, 33 | E: Into, 34 | { 35 | assert_eq!(T::type_info(), expected.into()); 36 | } 37 | 38 | macro_rules! assert_type { 39 | ( $ty:ty, $expected:expr ) => {{ 40 | assert_type::<$ty, _>($expected) 41 | }}; 42 | } 43 | 44 | #[test] 45 | fn primitives() { 46 | assert_type!(bool, TypeDefPrimitive::Bool); 47 | assert_type!(&str, TypeDefPrimitive::Str); 48 | assert_type!(i8, TypeDefPrimitive::I8); 49 | 50 | assert_type!([bool], TypeDefSequence::new(meta_type::())); 51 | } 52 | 53 | #[test] 54 | fn prelude_items() { 55 | assert_type!(String, TypeDefPrimitive::Str); 56 | 57 | assert_type!( 58 | Option, 59 | Type::builder() 60 | .path(Path::prelude("Option")) 61 | .type_params(named_type_params![(T, u128)]) 62 | .variant( 63 | Variants::new() 64 | .variant("None", |v| v.index(0)) 65 | .variant("Some", |v| { 66 | v.index(1) 67 | .fields(Fields::unnamed().field(|f| f.ty::())) 68 | }) 69 | ) 70 | ); 71 | assert_type!( 72 | Result, 73 | Type::builder() 74 | .path(Path::prelude("Result")) 75 | .type_params(named_type_params![(T, bool), (E, String)]) 76 | .variant( 77 | Variants::new() 78 | .variant( 79 | "Ok", |v| v 80 | .index(0) 81 | .fields(Fields::unnamed().field(|f| f.ty::())) 82 | ) 83 | .variant( 84 | "Err", |v| v 85 | .index(1) 86 | .fields(Fields::unnamed().field(|f| f.ty::())) 87 | ) 88 | ) 89 | ); 90 | assert_type!( 91 | Cow, 92 | Type::builder() 93 | .path(Path::prelude("Cow")) 94 | .type_params(named_type_params![(T, u128)]) 95 | .composite(Fields::unnamed().field(|f| f.ty::())) 96 | ); 97 | 98 | assert_type!( 99 | NonZeroU32, 100 | Type::builder() 101 | .path(Path::prelude("NonZeroU32")) 102 | .composite(Fields::unnamed().field(|f| f.ty::())) 103 | ) 104 | } 105 | 106 | #[test] 107 | fn phantom_data() { 108 | assert_type!( 109 | PhantomData, 110 | Type::builder() 111 | .path(Path::prelude("PhantomData")) 112 | .docs(&["PhantomData placeholder, this type should be filtered out"]) 113 | .composite(Fields::unit()) 114 | ) 115 | } 116 | 117 | #[test] 118 | fn collections() { 119 | assert_type!( 120 | BTreeMap, 121 | Type::builder() 122 | .path(Path::prelude("BTreeMap")) 123 | .type_params(named_type_params![(K, String), (V, u32)]) 124 | .composite(Fields::unnamed().field(|f| f.ty::<[(String, u32)]>())) 125 | ); 126 | 127 | assert_type!( 128 | BTreeSet, 129 | Type::builder() 130 | .path(Path::prelude("BTreeSet")) 131 | .type_params(named_type_params![(T, String)]) 132 | .composite(Fields::unnamed().field(|f| f.ty::<[String]>())) 133 | ); 134 | 135 | assert_type!( 136 | BinaryHeap, 137 | Type::builder() 138 | .path(Path::prelude("BinaryHeap")) 139 | .type_params(named_type_params![(T, String)]) 140 | .composite(Fields::unnamed().field(|f| f.ty::<[String]>())) 141 | ); 142 | 143 | assert_type!( 144 | std::collections::VecDeque, 145 | TypeDefSequence::new(meta_type::()) 146 | ); 147 | } 148 | 149 | #[cfg(feature = "bit-vec")] 150 | #[test] 151 | fn bitvec() { 152 | use bitvec::{ 153 | order::{Lsb0, Msb0}, 154 | vec::BitVec, 155 | }; 156 | 157 | assert_type!( 158 | BitVec, 159 | TypeDefBitSequence::new::() 160 | ); 161 | 162 | assert_type!( 163 | BitVec, 164 | TypeDefBitSequence::new::() 165 | ); 166 | 167 | assert_type!( 168 | BitVec, 169 | TypeDefBitSequence::new::() 170 | ); 171 | } 172 | 173 | #[test] 174 | fn scale_compact_types() { 175 | assert_type!(Compact, TypeDefCompact::new(meta_type::())) 176 | } 177 | 178 | #[test] 179 | fn tuple_primitives() { 180 | // unit 181 | assert_type!((), TypeDefTuple::new(tuple_meta_type!())); 182 | 183 | // tuple with one element 184 | assert_type!((bool,), TypeDefTuple::new(tuple_meta_type!(bool))); 185 | 186 | // tuple with multiple elements 187 | assert_type!( 188 | (bool, String), 189 | TypeDefTuple::new(tuple_meta_type!(bool, String)) 190 | ); 191 | 192 | // nested tuple 193 | assert_type!( 194 | ((i8, i16), (u32, u64)), 195 | TypeDefTuple::new(vec![meta_type::<(i8, i16)>(), meta_type::<(u32, u64)>(),]) 196 | ); 197 | } 198 | 199 | #[test] 200 | fn tuple_phantom_data_erased() { 201 | // nested tuple 202 | assert_type!( 203 | (u64, PhantomData), 204 | TypeDefTuple::new(vec![meta_type::(),]) 205 | ); 206 | } 207 | 208 | #[test] 209 | fn array_primitives() { 210 | // array 211 | assert_type!([bool; 3], TypeDefArray::new(3, meta_type::())); 212 | // nested 213 | assert_type!([[i32; 5]; 5], TypeDefArray::new(5, meta_type::<[i32; 5]>())); 214 | // sequence 215 | assert_type!([bool], TypeDefSequence::new(meta_type::())); 216 | // vec 217 | assert_type!(Vec, TypeDefSequence::new(meta_type::())); 218 | } 219 | 220 | #[test] 221 | fn struct_with_generics() { 222 | #[allow(unused)] 223 | struct MyStruct { 224 | data: T, 225 | } 226 | 227 | impl TypeInfo for MyStruct 228 | where 229 | T: TypeInfo + 'static, 230 | { 231 | type Identity = Self; 232 | 233 | fn type_info() -> Type { 234 | Type::builder() 235 | .path(Path::new("MyStruct", module_path!())) 236 | .type_params(type_params!(T)) 237 | .composite(Fields::named().field(|f| f.ty::().name("data").type_name("T"))) 238 | } 239 | } 240 | 241 | // Normal struct 242 | let struct_bool_type_info = Type::builder() 243 | .path(Path::from_segments(vec!["scale_info", "tests", "MyStruct"]).unwrap()) 244 | .type_params(named_type_params![(T, bool)]) 245 | .composite(Fields::named().field(|f| f.ty::().name("data").type_name("T"))); 246 | 247 | assert_type!(MyStruct, struct_bool_type_info); 248 | 249 | // With "`Self` typed" fields 250 | type SelfTyped = MyStruct>>; 251 | let expected_type = Type::builder() 252 | .path(Path::new("MyStruct", "scale_info::tests")) 253 | .type_params(named_type_params![(T, Box>)]) 254 | .composite( 255 | Fields::named().field(|f| f.ty::>>().name("data").type_name("T")), 256 | ); 257 | assert_type!(SelfTyped, expected_type); 258 | } 259 | 260 | #[test] 261 | fn basic_struct_with_phantoms() { 262 | #[allow(unused)] 263 | struct SomeStruct { 264 | a: u8, 265 | marker: PhantomData, 266 | } 267 | 268 | impl TypeInfo for SomeStruct 269 | where 270 | T: TypeInfo + 'static, 271 | { 272 | type Identity = Self; 273 | 274 | fn type_info() -> Type { 275 | Type::builder() 276 | .path(Path::new("SomeStruct", module_path!())) 277 | .type_params(type_params!(T)) 278 | .composite(Fields::named().field(|f| f.ty::().name("a").type_name("u8"))) 279 | } 280 | } 281 | 282 | let struct_bool_type_info = Type::builder() 283 | .path(Path::from_segments(vec!["scale_info", "tests", "SomeStruct"]).unwrap()) 284 | .type_params(named_type_params![(T, bool)]) 285 | .composite(Fields::named().field(|f| f.ty::().name("a").type_name("u8"))); 286 | 287 | assert_type!(SomeStruct, struct_bool_type_info); 288 | } 289 | 290 | #[test] 291 | fn basic_enum_with_index() { 292 | use scale::Encode; 293 | 294 | #[allow(unused)] 295 | #[derive(Encode)] 296 | enum IndexedRustEnum { 297 | #[codec(index = 4)] 298 | A(bool), 299 | #[codec(index = 0)] 300 | B { 301 | b: u8, 302 | }, 303 | C(u16, u32), 304 | D, 305 | } 306 | impl TypeInfo for IndexedRustEnum { 307 | type Identity = Self; 308 | 309 | fn type_info() -> Type { 310 | Type::builder() 311 | .path(Path::new("IndexedRustEnum", module_path!())) 312 | .variant( 313 | Variants::new() 314 | .variant("A", |v| { 315 | v.index(4).fields( 316 | Fields::unnamed().field(|f| f.ty::().type_name("bool")), 317 | ) 318 | }) 319 | .variant("B", |v| { 320 | v.index(0).fields( 321 | Fields::named().field(|f| f.ty::().name("b").type_name("u8")), 322 | ) 323 | }) 324 | .variant("C", |v| { 325 | v.index(2).fields( 326 | Fields::unnamed() 327 | .field(|f| f.ty::().type_name("u16")) 328 | .field(|f| f.ty::().type_name("u32")), 329 | ) 330 | }) 331 | .variant_unit("D", 3), 332 | ) 333 | } 334 | } 335 | 336 | let ty = Type::builder() 337 | .path(Path::new("IndexedRustEnum", module_path!())) 338 | .variant( 339 | Variants::new() 340 | .variant("A", |v| { 341 | v.index(4) 342 | .fields(Fields::unnamed().field(|f| f.ty::().type_name("bool"))) 343 | }) 344 | .variant("B", |v| { 345 | v.index(0) 346 | .fields(Fields::named().field(|f| f.ty::().name("b").type_name("u8"))) 347 | }) 348 | .variant("C", |v| { 349 | v.index(2).fields( 350 | Fields::unnamed() 351 | .field(|f| f.ty::().type_name("u16")) 352 | .field(|f| f.ty::().type_name("u32")), 353 | ) 354 | }) 355 | .variant_unit("D", 3), 356 | ); 357 | 358 | assert_type!(IndexedRustEnum, ty); 359 | } 360 | -------------------------------------------------------------------------------- /src/ty/composite.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::prelude::vec::Vec; 16 | 17 | use crate::{ 18 | form::{Form, MetaForm, PortableForm}, 19 | Field, IntoPortable, Registry, 20 | }; 21 | use derive_more::From; 22 | use scale::Encode; 23 | #[cfg(feature = "serde")] 24 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 25 | 26 | /// A composite type, consisting of either named (struct) or unnamed (tuple 27 | /// struct) fields 28 | /// 29 | /// # Examples 30 | /// 31 | /// ## A Rust struct with named fields. 32 | /// 33 | /// ``` 34 | /// struct Person { 35 | /// name: String, 36 | /// age_in_years: u8, 37 | /// friends: Vec, 38 | /// } 39 | /// ``` 40 | /// 41 | /// ## A tuple struct with unnamed fields. 42 | /// 43 | /// ``` 44 | /// struct Color(u8, u8, u8); 45 | /// ``` 46 | /// 47 | /// ## A so-called unit struct 48 | /// 49 | /// ``` 50 | /// struct JustAMarker; 51 | /// ``` 52 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 53 | #[cfg_attr( 54 | feature = "serde", 55 | serde(bound( 56 | serialize = "T::Type: Serialize, T::String: Serialize", 57 | deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", 58 | )) 59 | )] 60 | #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))] 61 | #[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))] 62 | #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] 63 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, From, Encode)] 64 | pub struct TypeDefComposite { 65 | /// The fields of the composite type. 66 | #[cfg_attr( 67 | feature = "serde", 68 | serde(skip_serializing_if = "Vec::is_empty", default) 69 | )] 70 | pub fields: Vec>, 71 | } 72 | 73 | impl IntoPortable for TypeDefComposite { 74 | type Output = TypeDefComposite; 75 | 76 | fn into_portable(self, registry: &mut Registry) -> Self::Output { 77 | TypeDefComposite { 78 | fields: registry.map_into_portable(self.fields), 79 | } 80 | } 81 | } 82 | 83 | impl TypeDefComposite 84 | where 85 | T: Form, 86 | { 87 | /// Creates a new struct definition with named fields. 88 | pub fn new(fields: I) -> Self 89 | where 90 | I: IntoIterator>, 91 | { 92 | Self { 93 | fields: fields.into_iter().collect(), 94 | } 95 | } 96 | } 97 | 98 | impl TypeDefComposite 99 | where 100 | T: Form, 101 | { 102 | /// Returns the fields of the composite type. 103 | #[deprecated( 104 | since = "2.5.0", 105 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 106 | )] 107 | pub fn fields(&self) -> &[Field] { 108 | &self.fields 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/ty/fields.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | build::FieldBuilder, 17 | form::{Form, MetaForm, PortableForm}, 18 | prelude::vec::Vec, 19 | IntoPortable, Registry, 20 | }; 21 | use scale::Encode; 22 | #[cfg(feature = "serde")] 23 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 24 | 25 | /// A field of a struct-like data type. 26 | /// 27 | /// Name is optional so it can represent both named and unnamed fields. 28 | /// 29 | /// This can be a named field of a struct type or an enum struct variant, or an 30 | /// unnamed field of a tuple struct. 31 | /// 32 | /// # Type name 33 | /// 34 | /// The `type_name` field contains a string which is the name of the type of the 35 | /// field as it appears in the source code. The exact contents and format of the 36 | /// type name are not specified, but in practice will be the name of any valid 37 | /// type for a field e.g. 38 | /// 39 | /// - Concrete types e.g `"u32"`, `"bool"`, `"Foo"` etc. 40 | /// - Type parameters e.g `"T"`, `"U"` 41 | /// - Generic types e.g `"Vec"`, `"Vec"` 42 | /// - Associated types e.g. `"T::MyType"`, `"::MyType"` 43 | /// - Type aliases e.g. `"MyTypeAlias"`, `"MyTypeAlias"` 44 | /// - Other built in Rust types e.g. arrays, references etc. 45 | /// 46 | /// Note that the type name doesn't correspond to the underlying type of the 47 | /// field, unless using a concrete type directly. Any given type may be referred 48 | /// to by multiple field type names, when using generic type parameters and type 49 | /// aliases. 50 | /// 51 | /// This is intended for informational and diagnostic purposes only. Although it 52 | /// is possible to infer certain properties e.g. whether a type name is a type 53 | /// alias, there are no guarantees provided, and the type name representation 54 | /// may change. 55 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 56 | #[cfg_attr( 57 | feature = "serde", 58 | serde(bound( 59 | serialize = "T::Type: Serialize, T::String: Serialize", 60 | deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", 61 | )) 62 | )] 63 | #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 64 | #[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))] 65 | #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] 66 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Encode)] 67 | pub struct Field { 68 | /// The name of the field. None for unnamed fields. 69 | #[cfg_attr( 70 | feature = "serde", 71 | serde(skip_serializing_if = "Option::is_none", default) 72 | )] 73 | pub name: Option, 74 | /// The type of the field. 75 | #[cfg_attr(feature = "serde", serde(rename = "type"))] 76 | pub ty: T::Type, 77 | /// The name of the type of the field as it appears in the source code. 78 | #[cfg_attr( 79 | feature = "serde", 80 | serde(skip_serializing_if = "Option::is_none", default) 81 | )] 82 | pub type_name: Option, 83 | /// Documentation 84 | #[cfg_attr( 85 | feature = "serde", 86 | serde(skip_serializing_if = "Vec::is_empty", default) 87 | )] 88 | pub docs: Vec, 89 | } 90 | 91 | impl IntoPortable for Field { 92 | type Output = Field; 93 | 94 | fn into_portable(self, registry: &mut Registry) -> Self::Output { 95 | Field { 96 | name: self.name.map(Into::into), 97 | ty: registry.register_type(&self.ty), 98 | type_name: self.type_name.map(Into::into), 99 | docs: self.docs.into_iter().map(Into::into).collect(), 100 | } 101 | } 102 | } 103 | 104 | impl Field 105 | where 106 | T: Form, 107 | { 108 | /// Returns a new [`FieldBuilder`] for constructing a field. 109 | pub fn builder() -> FieldBuilder { 110 | FieldBuilder::new() 111 | } 112 | 113 | /// Creates a new field. 114 | /// 115 | /// Use this constructor if you want to instantiate from a given meta type. 116 | pub fn new( 117 | name: Option, 118 | ty: T::Type, 119 | type_name: Option, 120 | docs: Vec, 121 | ) -> Self { 122 | Self { 123 | name, 124 | ty, 125 | type_name, 126 | docs, 127 | } 128 | } 129 | } 130 | 131 | impl Field 132 | where 133 | T: Form, 134 | { 135 | /// Returns the name of the field. None for unnamed fields. 136 | #[deprecated( 137 | since = "2.5.0", 138 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 139 | )] 140 | pub fn name(&self) -> Option<&T::String> { 141 | self.name.as_ref() 142 | } 143 | 144 | /// Returns the type of the field. 145 | #[deprecated( 146 | since = "2.5.0", 147 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 148 | )] 149 | pub fn ty(&self) -> &T::Type { 150 | &self.ty 151 | } 152 | 153 | /// Returns a string which is the name of the type of the field as it 154 | /// appears in the source code. The exact contents and format of the type 155 | /// name are not specified, but in practice will be the name of any valid 156 | /// type for a field. This is intended for informational and diagnostic 157 | /// purposes only. 158 | #[deprecated( 159 | since = "2.5.0", 160 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 161 | )] 162 | pub fn type_name(&self) -> Option<&T::String> { 163 | self.type_name.as_ref() 164 | } 165 | 166 | /// Returns the documentation of the field. 167 | #[deprecated( 168 | since = "2.5.0", 169 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 170 | )] 171 | pub fn docs(&self) -> &[T::String] { 172 | &self.docs 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/ty/path.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::prelude::{ 16 | fmt::{Display, Error as FmtError, Formatter}, 17 | iter, 18 | vec::Vec, 19 | }; 20 | 21 | use crate::{ 22 | form::{Form, MetaForm, PortableForm}, 23 | utils::is_rust_identifier, 24 | IntoPortable, Registry, 25 | }; 26 | use scale::Encode; 27 | #[cfg(feature = "serde")] 28 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 29 | 30 | /// Represents the path of a type definition. 31 | /// 32 | /// This consists of several segments that each have to be a valid Rust 33 | /// identifier. The first segment represents the crate name in which the type 34 | /// has been defined. The last segment is the identifier accessed with `ident()`. 35 | /// 36 | /// Rust prelude type may have an empty namespace definition. 37 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 38 | #[cfg_attr( 39 | feature = "serde", 40 | serde(bound( 41 | serialize = "T::Type: Serialize, T::String: Serialize", 42 | deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", 43 | )) 44 | )] 45 | #[cfg_attr(feature = "serde", serde(transparent))] 46 | #[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))] 47 | #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] 48 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Encode)] 49 | pub struct Path { 50 | /// The segments of the namespace. 51 | pub segments: Vec, 52 | } 53 | 54 | impl Default for Path 55 | where 56 | T: Form, 57 | { 58 | fn default() -> Self { 59 | Path { 60 | segments: Vec::new(), 61 | } 62 | } 63 | } 64 | 65 | impl IntoPortable for Path { 66 | type Output = Path; 67 | 68 | fn into_portable(self, _registry: &mut Registry) -> Self::Output { 69 | Path { 70 | segments: self.segments.into_iter().map(Into::into).collect(), 71 | } 72 | } 73 | } 74 | 75 | impl Display for Path { 76 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 77 | write!(f, "{}", self.segments.join("::")) 78 | } 79 | } 80 | 81 | impl Path { 82 | /// Create a new Path 83 | /// 84 | /// # Panics 85 | /// 86 | /// - If the type identifier or module path contain invalid Rust identifiers 87 | pub fn new(ident: &'static str, module_path: &'static str) -> Path { 88 | let segments = module_path.split("::"); 89 | Self::from_segments(segments.chain(iter::once(ident))) 90 | .expect("All path segments should be valid Rust identifiers") 91 | } 92 | 93 | /// Create a new Path 94 | /// 95 | /// The `segment_replace` is a list of `(search, replace)` items. Every 96 | /// `search` item that appears in the `module_path` is replaced by the 97 | /// `replace` item. This can be used for example to replace the crate name 98 | /// or even the name of the type in the final [`Path`]. 99 | /// 100 | /// # Panics 101 | /// 102 | /// - If the type identifier, module path or replace contain invalid Rust identifiers 103 | pub fn new_with_replace( 104 | ident: &'static str, 105 | module_path: &'static str, 106 | segment_replace: &[(&'static str, &'static str)], 107 | ) -> Path { 108 | let segments = module_path.split("::"); 109 | Self::from_segments( 110 | segments 111 | .chain(iter::once(ident)) 112 | .map(|s| segment_replace.iter().find(|r| s == r.0).map_or(s, |r| r.1)), 113 | ) 114 | .expect("All path segments should be valid Rust identifiers") 115 | } 116 | 117 | /// Create a Path from the given segments 118 | /// 119 | /// # Errors 120 | /// 121 | /// - If no segments are supplied 122 | /// - If any of the segments are invalid Rust identifiers 123 | pub fn from_segments(segments: I) -> Result 124 | where 125 | I: IntoIterator::String>, 126 | { 127 | let segments = segments.into_iter().collect::>(); 128 | if segments.is_empty() { 129 | return Err(PathError::MissingSegments); 130 | } 131 | if let Some(err_at) = segments.iter().position(|seg| !is_rust_identifier(seg)) { 132 | return Err(PathError::InvalidIdentifier { segment: err_at }); 133 | } 134 | Ok(Path { segments }) 135 | } 136 | 137 | /// Crate a Path for types in the Prelude namespace 138 | /// 139 | /// # Panics 140 | /// 141 | /// - If the supplied ident is not a valid Rust identifier 142 | pub(crate) fn prelude(ident: ::String) -> Self { 143 | Self::from_segments([ident]) 144 | .unwrap_or_else(|_| panic!("{ident:?} is not a valid Rust identifier")) 145 | } 146 | } 147 | 148 | impl Path 149 | where 150 | T: Form, 151 | { 152 | /// Create an empty path for types which shall not be named 153 | #[allow(unused)] 154 | pub(crate) fn voldemort() -> Self { 155 | Self { 156 | segments: Vec::new(), 157 | } 158 | } 159 | 160 | /// Create a Path from the given segments. 161 | /// 162 | /// Does *not* check that the segments are valid Rust identifiers. 163 | pub fn from_segments_unchecked(segments: I) -> Path 164 | where 165 | I: IntoIterator, 166 | { 167 | Self { 168 | segments: segments.into_iter().collect(), 169 | } 170 | } 171 | 172 | /// Returns the segments of the Path 173 | #[deprecated( 174 | since = "2.5.0", 175 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 176 | )] 177 | pub fn segments(&self) -> &[T::String] { 178 | &self.segments 179 | } 180 | 181 | /// Returns `true` if the path is empty 182 | pub fn is_empty(&self) -> bool { 183 | self.segments.is_empty() 184 | } 185 | 186 | /// Get the ident segment of the Path 187 | pub fn ident(&self) -> Option { 188 | self.segments.iter().last().cloned() 189 | } 190 | 191 | /// Get the namespace segments of the Path 192 | pub fn namespace(&self) -> &[T::String] { 193 | self.segments.split_last().map(|(_, ns)| ns).unwrap_or(&[]) 194 | } 195 | } 196 | 197 | /// An error that may be encountered upon constructing namespaces. 198 | #[derive(PartialEq, Eq, Debug)] 199 | pub enum PathError { 200 | /// If the module path does not at least have one segment. 201 | MissingSegments, 202 | /// If a segment within a module path is not a proper Rust identifier. 203 | InvalidIdentifier { 204 | /// The index of the erroneous segment. 205 | segment: usize, 206 | }, 207 | } 208 | 209 | #[cfg(test)] 210 | mod tests { 211 | use super::*; 212 | 213 | #[test] 214 | fn path_ok() { 215 | assert_eq!( 216 | Path::from_segments(vec!["hello"]), 217 | Ok(Path { 218 | segments: vec!["hello"] 219 | }) 220 | ); 221 | assert_eq!( 222 | Path::from_segments(vec!["Hello", "World"]), 223 | Ok(Path { 224 | segments: vec!["Hello", "World"] 225 | }) 226 | ); 227 | assert_eq!( 228 | Path::from_segments(vec!["_"]), 229 | Ok(Path { 230 | segments: vec!["_"] 231 | }) 232 | ); 233 | } 234 | 235 | #[test] 236 | fn path_with_raw_identifers_ok() { 237 | assert_eq!( 238 | Path::from_segments(vec!["r#mod", "r#Struct"]), 239 | Ok(Path { 240 | segments: vec!["r#mod", "r#Struct"] 241 | }) 242 | ); 243 | } 244 | 245 | #[test] 246 | fn path_err() { 247 | assert_eq!( 248 | Path::from_segments(Vec::new()), 249 | Err(PathError::MissingSegments) 250 | ); 251 | assert_eq!( 252 | Path::from_segments(vec![""]), 253 | Err(PathError::InvalidIdentifier { segment: 0 }) 254 | ); 255 | assert_eq!( 256 | Path::from_segments(vec!["1"]), 257 | Err(PathError::InvalidIdentifier { segment: 0 }) 258 | ); 259 | assert_eq!( 260 | Path::from_segments(vec!["Hello", ", World!"]), 261 | Err(PathError::InvalidIdentifier { segment: 1 }) 262 | ); 263 | } 264 | 265 | #[test] 266 | fn path_from_module_path_and_ident() { 267 | assert_eq!( 268 | Path::new("Planet", "hello::world"), 269 | Path { 270 | segments: vec!["hello", "world", "Planet"] 271 | } 272 | ); 273 | assert_eq!( 274 | Path::from_segments(vec!["Earth", "::world"]), 275 | Err(PathError::InvalidIdentifier { segment: 1 }) 276 | ); 277 | } 278 | 279 | #[test] 280 | fn path_get_namespace_and_ident() { 281 | let path = Path::new("Planet", "hello::world"); 282 | assert_eq!(path.namespace(), &["hello", "world"]); 283 | assert_eq!(path.ident(), Some("Planet")); 284 | } 285 | 286 | #[test] 287 | #[should_panic] 288 | fn path_new_panics_with_invalid_identifiers() { 289 | Path::new("Planet", "hello$!@$::world"); 290 | } 291 | 292 | #[test] 293 | fn path_display() { 294 | let path = Path::new("Planet", "hello::world").into_portable(&mut Default::default()); 295 | assert_eq!("hello::world::Planet", format!("{}", path)) 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/ty/variant.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::prelude::vec::Vec; 16 | 17 | use crate::{ 18 | form::{Form, MetaForm, PortableForm}, 19 | Field, IntoPortable, Registry, 20 | }; 21 | use derive_more::From; 22 | use scale::Encode; 23 | #[cfg(feature = "serde")] 24 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 25 | 26 | /// A Enum type (consisting of variants). 27 | /// 28 | /// # Examples 29 | /// 30 | /// ## A Rust enum, aka tagged union. 31 | /// 32 | /// ``` 33 | /// enum MyEnum { 34 | /// RustAllowsForClikeVariants, 35 | /// AndAlsoForTupleStructs(i32, bool), 36 | /// OrStructs { 37 | /// with: i32, 38 | /// named: bool, 39 | /// fields: [u8; 32], 40 | /// }, 41 | /// ItIsntPossibleToSetADiscriminantThough, 42 | /// } 43 | /// ``` 44 | /// 45 | /// ## A C-like enum type. 46 | /// 47 | /// ``` 48 | /// enum Days { 49 | /// Monday, 50 | /// Tuesday, 51 | /// Wednesday, 52 | /// Thursday = 42, // Allows setting the discriminant explicitly 53 | /// Friday, 54 | /// Saturday, 55 | /// Sunday, 56 | /// } 57 | /// ``` 58 | /// 59 | /// ## An empty enum (for marker purposes) 60 | /// 61 | /// ``` 62 | /// enum JustAMarker {} 63 | /// ``` 64 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 65 | #[cfg_attr( 66 | feature = "serde", 67 | serde(bound( 68 | serialize = "T::Type: Serialize, T::String: Serialize", 69 | deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", 70 | )) 71 | )] 72 | #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))] 73 | #[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))] 74 | #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] 75 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, From, Encode)] 76 | pub struct TypeDefVariant { 77 | /// The variants of a variant type 78 | #[cfg_attr( 79 | feature = "serde", 80 | serde(skip_serializing_if = "Vec::is_empty", default) 81 | )] 82 | pub variants: Vec>, 83 | } 84 | 85 | impl IntoPortable for TypeDefVariant { 86 | type Output = TypeDefVariant; 87 | 88 | fn into_portable(self, registry: &mut Registry) -> Self::Output { 89 | TypeDefVariant { 90 | variants: registry.map_into_portable(self.variants), 91 | } 92 | } 93 | } 94 | 95 | impl TypeDefVariant 96 | where 97 | T: Form, 98 | { 99 | /// Create a new `TypeDefVariant` with the given variants 100 | pub fn new(variants: I) -> Self 101 | where 102 | I: IntoIterator>, 103 | { 104 | Self { 105 | variants: variants.into_iter().collect(), 106 | } 107 | } 108 | } 109 | 110 | impl TypeDefVariant 111 | where 112 | T: Form, 113 | { 114 | /// Returns the variants of a variant type 115 | #[deprecated( 116 | since = "2.5.0", 117 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 118 | )] 119 | pub fn variants(&self) -> &[Variant] { 120 | &self.variants 121 | } 122 | } 123 | 124 | /// A struct enum variant with either named (struct) or unnamed (tuple struct) 125 | /// fields. 126 | /// 127 | /// # Example 128 | /// 129 | /// ``` 130 | /// enum Operation { 131 | /// Zero, 132 | /// // ^^^^ this is a unit struct enum variant 133 | /// Add(i32, i32), 134 | /// // ^^^^^^^^^^^^^ this is a tuple-struct enum variant 135 | /// Minus { source: i32 } 136 | /// // ^^^^^^^^^^^^^^^^^^^^^ this is a struct enum variant 137 | /// } 138 | /// ``` 139 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 140 | #[cfg_attr( 141 | feature = "serde", 142 | serde(bound( 143 | serialize = "T::Type: Serialize, T::String: Serialize", 144 | deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", 145 | )) 146 | )] 147 | #[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))] 148 | #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] 149 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Encode)] 150 | pub struct Variant { 151 | /// The name of the variant. 152 | pub name: T::String, 153 | /// The fields of the variant. 154 | #[cfg_attr( 155 | feature = "serde", 156 | serde(skip_serializing_if = "Vec::is_empty", default) 157 | )] 158 | pub fields: Vec>, 159 | /// Index of the variant, used in `parity-scale-codec`. 160 | /// 161 | /// The value of this will be, in order of precedence: 162 | /// 1. The explicit index defined by a `#[codec(index = N)]` attribute. 163 | /// 2. The implicit index from the position of the variant in the `enum` definition. 164 | pub index: u8, 165 | /// Documentation 166 | #[cfg_attr( 167 | feature = "serde", 168 | serde(skip_serializing_if = "Vec::is_empty", default) 169 | )] 170 | pub docs: Vec, 171 | } 172 | 173 | impl IntoPortable for Variant { 174 | type Output = Variant; 175 | 176 | fn into_portable(self, registry: &mut Registry) -> Self::Output { 177 | Variant { 178 | name: self.name.into(), 179 | fields: registry.map_into_portable(self.fields), 180 | index: self.index, 181 | docs: self.docs.into_iter().map(Into::into).collect(), 182 | } 183 | } 184 | } 185 | 186 | impl Variant 187 | where 188 | T: Form, 189 | { 190 | /// Creates a new variant. 191 | pub fn new(name: T::String, fields: Vec>, index: u8, docs: Vec) -> Self { 192 | Self { 193 | name, 194 | fields, 195 | index, 196 | docs, 197 | } 198 | } 199 | } 200 | 201 | impl Variant 202 | where 203 | T: Form, 204 | { 205 | /// Returns the name of the variant. 206 | #[deprecated( 207 | since = "2.5.0", 208 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 209 | )] 210 | pub fn name(&self) -> &T::String { 211 | &self.name 212 | } 213 | 214 | /// Returns the fields of the struct variant. 215 | #[deprecated( 216 | since = "2.5.0", 217 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 218 | )] 219 | pub fn fields(&self) -> &[Field] { 220 | &self.fields 221 | } 222 | 223 | /// Returns the index of the variant. 224 | #[deprecated( 225 | since = "2.5.0", 226 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 227 | )] 228 | pub fn index(&self) -> u8 { 229 | self.index 230 | } 231 | 232 | /// Returns the documentation of the variant. 233 | #[deprecated( 234 | since = "2.5.0", 235 | note = "Prefer to access the fields directly; this getter will be removed in the next major version" 236 | )] 237 | pub fn docs(&self) -> &[T::String] { 238 | &self.docs 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Returns `true` if the given string is a proper Rust identifier. 16 | pub fn is_rust_identifier(s: &str) -> bool { 17 | // Only ascii encoding is allowed. 18 | // Note: Maybe this check is superseded by the `head` and `tail` check. 19 | if !s.is_ascii() { 20 | return false; 21 | } 22 | // Trim valid raw identifier prefix 23 | let trimmed = s.trim_start_matches("r#"); 24 | if let Some((&head, tail)) = trimmed.as_bytes().split_first() { 25 | // Check if head and tail make up a proper Rust identifier. 26 | let head_ok = head == b'_' || head.is_ascii_lowercase() || head.is_ascii_uppercase(); 27 | let tail_ok = tail.iter().all(|&ch| { 28 | ch == b'_' || ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() 29 | }); 30 | head_ok && tail_ok 31 | } else { 32 | // String is empty and thus not a valid Rust identifier. 33 | false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test_suite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scale-info-test-suite" 3 | version = "0.0.0" 4 | authors = [ 5 | "Parity Technologies ", 6 | "Centrality Developers ", 7 | ] 8 | edition = "2021" 9 | publish = false 10 | 11 | license = "Apache-2.0" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | # Deliberately renamed from scale-info to test that `crate` attr works. 17 | info = { package = "scale-info", path = "..", features = ["derive", "serde", "decode"] } 18 | 19 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } 20 | serde = "1.0" 21 | serde_json = "1.0" 22 | pretty_assertions = "0.6.1" 23 | trybuild = "1.0.79" 24 | -------------------------------------------------------------------------------- /test_suite/derive_tests_no_std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scale-info-derive-tests-no-std" 3 | version = "0.0.0" 4 | authors = ["Parity Technologies ", "Centrality Developers "] 5 | edition = "2021" 6 | publish = false 7 | 8 | license = "Apache-2.0" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | scale-info = { path = "../..", default-features = false, features = ["derive", "bit-vec", "decode"] } 14 | scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] } 15 | bitvec = { version = "1", default-features = false, features = ["alloc"] } 16 | libc = { version = "0.2", default-features = false } 17 | libc_alloc = { version = "1.0.6" } 18 | 19 | [profile.dev] 20 | panic = "abort" 21 | 22 | [profile.release] 23 | panic = "abort" 24 | 25 | [workspace] 26 | -------------------------------------------------------------------------------- /test_suite/derive_tests_no_std/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /test_suite/derive_tests_no_std/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #![allow(internal_features)] 15 | #![feature(lang_items, start)] 16 | #![feature(alloc_error_handler)] 17 | #![no_std] 18 | 19 | #[start] 20 | fn start(_argc: isize, _argv: *const *const u8) -> isize { 21 | test(); 22 | 0 23 | } 24 | 25 | #[lang = "eh_personality"] 26 | #[no_mangle] 27 | pub extern "C" fn rust_eh_personality() {} 28 | 29 | #[panic_handler] 30 | fn panic(_info: &core::panic::PanicInfo) -> ! { 31 | unsafe { 32 | libc::abort(); 33 | } 34 | } 35 | 36 | use libc_alloc::LibcAlloc; 37 | 38 | #[global_allocator] 39 | static ALLOCATOR: LibcAlloc = LibcAlloc; 40 | 41 | ////////////////////////////////////////////////////////////////////////////// 42 | 43 | // Note: Use the types in some way to make sure they are not pruned as dead code. 44 | // If an assert fails we will get `Aborted (core dumped)`. 45 | fn test() { 46 | assert_eq!(UnitStruct::type_info().type_params.len(), 0); 47 | assert_eq!(TupleStruct::type_info().type_params.len(), 0); 48 | assert_eq!(Struct::::type_info().type_params.len(), 1); 49 | assert_eq!(CLike::type_info().type_params.len(), 0); 50 | assert_eq!(E::::type_info().type_params.len(), 1); 51 | } 52 | 53 | use bitvec::{order::Lsb0, vec::BitVec}; 54 | use scale::{Decode, Encode}; 55 | use scale_info::TypeInfo; 56 | 57 | #[allow(unused)] 58 | #[derive(TypeInfo, Decode, Encode)] 59 | struct UnitStruct; 60 | 61 | #[allow(unused)] 62 | #[derive(TypeInfo, Decode, Encode)] 63 | struct TupleStruct(u128, bool); 64 | 65 | #[allow(unused)] 66 | #[derive(TypeInfo, Decode, Encode)] 67 | struct Struct { 68 | t: T, 69 | bitvec: BitVec, 70 | } 71 | 72 | #[allow(unused)] 73 | #[derive(TypeInfo, Decode, Encode)] 74 | enum CLike { 75 | A, 76 | B, 77 | C, 78 | } 79 | 80 | #[allow(unused)] 81 | #[derive(TypeInfo, Decode, Encode)] 82 | enum E { 83 | A(T), 84 | B { b: T }, 85 | C, 86 | } 87 | -------------------------------------------------------------------------------- /test_suite/tests/codec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_std] 16 | #![allow(unused)] 17 | #![allow(dead_code)] 18 | 19 | use info::{self as scale_info}; 20 | use pretty_assertions::{assert_eq, assert_ne}; 21 | use scale::{Decode, Encode}; 22 | use scale_info::{ 23 | form::PortableForm, 24 | prelude::{num::NonZeroU32, string::String, vec, vec::Vec}, 25 | IntoPortable as _, MetaType, PortableRegistry, Registry, TypeInfo, 26 | }; 27 | 28 | #[derive(TypeInfo)] 29 | struct A { 30 | a: bool, 31 | b: Result, 32 | c: T, 33 | } 34 | 35 | #[derive(TypeInfo)] 36 | enum B { 37 | A, 38 | B(A), 39 | C { d: [u8; 32] }, 40 | } 41 | 42 | #[test] 43 | fn scale_encode_then_decode_to_readonly() { 44 | let mut registry = Registry::new(); 45 | registry.register_type(&MetaType::new::>()); 46 | 47 | let registry: PortableRegistry = registry.into(); 48 | let mut encoded = registry.encode(); 49 | let original_serialized = serde_json::to_value(registry).unwrap(); 50 | 51 | let readonly_decoded = PortableRegistry::decode(&mut &encoded[..]).unwrap(); 52 | assert!(readonly_decoded.resolve(0).is_some()); 53 | let decoded_serialized = serde_json::to_value(readonly_decoded).unwrap(); 54 | 55 | assert_eq!(decoded_serialized, original_serialized); 56 | } 57 | 58 | #[test] 59 | fn json_serialize_then_deserialize_to_readonly() { 60 | let mut registry = Registry::new(); 61 | registry.register_type(&MetaType::new::>()); 62 | 63 | let registry: PortableRegistry = registry.into(); 64 | let original_serialized = serde_json::to_value(registry).unwrap(); 65 | // assert_eq!(original_serialized, serde_json::Value::Null); 66 | let readonly_deserialized: PortableRegistry = 67 | serde_json::from_value(original_serialized.clone()).unwrap(); 68 | assert!(readonly_deserialized.resolve(0).is_some()); 69 | let readonly_serialized = serde_json::to_value(readonly_deserialized).unwrap(); 70 | 71 | assert_eq!(readonly_serialized, original_serialized); 72 | } 73 | -------------------------------------------------------------------------------- /test_suite/tests/ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[test] 16 | fn ui_tests() { 17 | let t = trybuild::TestCases::new(); 18 | t.compile_fail("tests/ui/fail_*.rs"); 19 | t.pass("tests/ui/pass_*.rs"); 20 | } 21 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_custom_bounds_missing_skip_type_params.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use core::marker::PhantomData; 4 | 5 | #[derive(TypeInfo)] 6 | #[scale_info(bounds())] 7 | struct A { 8 | marker: PhantomData, 9 | } 10 | 11 | fn main() {} -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_custom_bounds_missing_skip_type_params.stderr: -------------------------------------------------------------------------------- 1 | error: Type parameter requires a `TypeInfo` bound, so either: 2 | - add it to `#[scale_info(bounds(T: TypeInfo))]` 3 | - skip it with `#[scale_info(skip_type_params(T))]` 4 | --> tests/ui/fail_custom_bounds_missing_skip_type_params.rs:7:10 5 | | 6 | 7 | struct A { 7 | | ^ 8 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_duplicate_bounds_params.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use core::marker::PhantomData; 4 | 5 | #[derive(TypeInfo)] 6 | #[scale_info(bounds(), bounds())] 7 | struct A { 8 | marker: PhantomData, 9 | } 10 | 11 | fn main() {} -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_duplicate_bounds_params.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicate `bounds` attributes 2 | --> tests/ui/fail_duplicate_bounds_params.rs:6:1 3 | | 4 | 6 | #[scale_info(bounds(), bounds())] 5 | | ^ 6 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_missing_derive.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | enum PawType { 5 | Big(Paw), 6 | Small(Paw), 7 | } 8 | #[derive(TypeInfo)] 9 | #[scale_info(crate = info)] 10 | struct Cat { 11 | tail: Tail, 12 | ears: [Ear; 3], 13 | paws: PawType, 14 | } 15 | 16 | fn assert_type_info() {} 17 | 18 | fn main() { 19 | assert_type_info::>(); 20 | } 21 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_missing_derive.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `PawType: TypeInfo` is not satisfied 2 | --> tests/ui/fail_missing_derive.rs:19:24 3 | | 4 | 19 | assert_type_info::>(); 5 | | ^^^^^^^^^^^^^^^^^^ the trait `TypeInfo` is not implemented for `PawType` 6 | | 7 | = help: the following other types implement trait `TypeInfo`: 8 | &T 9 | &mut T 10 | () 11 | (A, B) 12 | (A, B, C) 13 | (A, B, C, D) 14 | (A, B, C, D, E) 15 | (A, B, C, D, E, F) 16 | and $N others 17 | note: required for `Cat` to implement `TypeInfo` 18 | --> tests/ui/fail_missing_derive.rs:8:10 19 | | 20 | 8 | #[derive(TypeInfo)] 21 | | ^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro 22 | 9 | #[scale_info(crate = info)] 23 | 10 | struct Cat { 24 | | ^^^^^^^^^^^^^^^^^^^ 25 | note: required by a bound in `assert_type_info` 26 | --> tests/ui/fail_missing_derive.rs:16:24 27 | | 28 | 16 | fn assert_type_info() {} 29 | | ^^^^^^^^ required by this bound in `assert_type_info` 30 | = note: this error originates in the derive macro `TypeInfo` (in Nightly builds, run with -Z macro-backtrace for more info) 31 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_unions.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[derive(TypeInfo)] 5 | #[repr(C)] 6 | union Commonwealth { 7 | a: u8, 8 | b: f32, 9 | } 10 | 11 | fn assert_type_info() {} 12 | 13 | fn main() { 14 | assert_type_info::(); 15 | } 16 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_unions.stderr: -------------------------------------------------------------------------------- 1 | error: Unions not supported 2 | --> tests/ui/fail_unions.rs:5:1 3 | | 4 | 5 | / #[repr(C)] 5 | 6 | | union Commonwealth { 6 | 7 | | a: u8, 7 | 8 | | b: f32, 8 | 9 | | } 9 | | |_^ 10 | 11 | error[E0277]: the trait bound `Commonwealth: TypeInfo` is not satisfied 12 | --> tests/ui/fail_unions.rs:14:24 13 | | 14 | 14 | assert_type_info::(); 15 | | ^^^^^^^^^^^^ the trait `TypeInfo` is not implemented for `Commonwealth` 16 | | 17 | = help: the following other types implement trait `TypeInfo`: 18 | &T 19 | &mut T 20 | () 21 | (A, B) 22 | (A, B, C) 23 | (A, B, C, D) 24 | (A, B, C, D, E) 25 | (A, B, C, D, E, F) 26 | and $N others 27 | note: required by a bound in `assert_type_info` 28 | --> tests/ui/fail_unions.rs:11:24 29 | | 30 | 11 | fn assert_type_info() {} 31 | | ^^^^^^^^ required by this bound in `assert_type_info` 32 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_capture_docs_attr.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use scale::Encode; 4 | 5 | #[derive(TypeInfo, Encode)] 6 | #[scale_info(capture_docs = "invalid")] 7 | /// Docs 8 | struct InvalidDocsCapture { 9 | /// Docs 10 | a: u8, 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_capture_docs_attr.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid capture_docs value. Expected one of: "default", "always", "never" 2 | --> tests/ui/fail_with_invalid_capture_docs_attr.rs:6:29 3 | | 4 | 6 | #[scale_info(capture_docs = "invalid")] 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_codec_attrs.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use scale::Encode; 4 | 5 | #[derive(TypeInfo, Encode)] 6 | struct NoMultipleAttrs { 7 | a: u8, 8 | #[codec(skip, compact)] 9 | b: u16, 10 | } 11 | 12 | #[derive(Encode, TypeInfo)] 13 | enum NoIndexOnVariantFields { 14 | Thing(#[codec(index = 3)] u32), 15 | } 16 | 17 | #[derive(Encode, TypeInfo)] 18 | enum IndexMustBeNumber { 19 | #[codec(index = a)] 20 | Thing(u32), 21 | } 22 | 23 | #[derive(Encode, TypeInfo)] 24 | enum EncodeAsAttrMustBeLiteral { 25 | #[codec(encode_as = u8, compact)] 26 | Thong(bool), 27 | } 28 | 29 | fn main() {} 30 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_codec_attrs.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid attribute on field, only `#[codec(skip)]`, `#[codec(compact)]` and `#[codec(encoded_as = "$EncodeAs")]` are accepted. 2 | --> tests/ui/fail_with_invalid_codec_attrs.rs:8:7 3 | | 4 | 8 | #[codec(skip, compact)] 5 | | ^^^^^ 6 | 7 | error: Invalid attribute on field, only `#[codec(skip)]`, `#[codec(compact)]` and `#[codec(encoded_as = "$EncodeAs")]` are accepted. 8 | --> tests/ui/fail_with_invalid_codec_attrs.rs:14:19 9 | | 10 | 14 | Thing(#[codec(index = 3)] u32), 11 | | ^^^^^ 12 | 13 | error: Invalid attribute on variant, only `#[codec(skip)]` and `#[codec(index = $u8)]` are accepted. 14 | --> tests/ui/fail_with_invalid_codec_attrs.rs:19:13 15 | | 16 | 19 | #[codec(index = a)] 17 | | ^^^^^ 18 | 19 | error: Invalid attribute on variant, only `#[codec(skip)]` and `#[codec(index = $u8)]` are accepted. 20 | --> tests/ui/fail_with_invalid_codec_attrs.rs:25:7 21 | | 22 | 25 | #[codec(encode_as = u8, compact)] 23 | | ^^^^^ 24 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_scale_info_attrs.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use scale::Encode; 4 | 5 | #[derive(TypeInfo, Encode)] 6 | #[scale_info(foo)] 7 | struct InvalidKeywordInScaleInfoAttr { 8 | a: u8, 9 | b: u16, 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /test_suite/tests/ui/fail_with_invalid_scale_info_attrs.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of: `bounds`, `skip_type_params`, `capture_docs`, `crate`, `replace_segment` 2 | --> tests/ui/fail_with_invalid_scale_info_attrs.rs:6:14 3 | | 4 | 6 | #[scale_info(foo)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_basic_generic_type.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[allow(dead_code)] 5 | #[derive(TypeInfo)] 6 | enum PawType { 7 | Big(Paw), 8 | Small(Paw), 9 | } 10 | #[derive(TypeInfo)] 11 | struct Cat { 12 | _tail: Tail, 13 | _ears: [Ear; 3], 14 | _paws: PawType, 15 | } 16 | 17 | fn assert_type_info() {} 18 | 19 | fn main() { 20 | assert_type_info::>(); 21 | } 22 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_combined_attributes.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[allow(unused)] 5 | #[derive(TypeInfo)] 6 | #[scale_info(bounds(), skip_type_params(T))] 7 | struct A { 8 | marker: core::marker::PhantomData, 9 | } 10 | 11 | #[allow(unused)] 12 | #[derive(TypeInfo)] 13 | #[scale_info(bounds())] 14 | #[scale_info(skip_type_params(T))] 15 | struct B { 16 | marker: core::marker::PhantomData, 17 | } 18 | 19 | fn main() { } -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_complex_generic_self_referential_type.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | 5 | #[derive(TypeInfo)] 6 | struct Nested

{ 7 | _pos: P, 8 | } 9 | 10 | #[derive(TypeInfo)] 11 | struct Is { 12 | _nested: N, 13 | } 14 | 15 | #[derive(TypeInfo)] 16 | struct That { 17 | _is: I, 18 | _selfie: S, 19 | } 20 | 21 | #[derive(TypeInfo)] 22 | struct Thing { 23 | _that: T, 24 | } 25 | 26 | #[derive(TypeInfo)] 27 | struct Other { 28 | _thing: T, 29 | } 30 | 31 | #[derive(TypeInfo)] 32 | struct Selfie { 33 | _another: Box>, 34 | _pos: Pos, 35 | _nested: Box>, Selfie>>>>, 36 | } 37 | 38 | fn assert_type_info() {} 39 | 40 | fn main() { 41 | assert_type_info::>(); 42 | } 43 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_custom_bounds.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use core::marker::PhantomData; 4 | 5 | #[allow(unused)] 6 | #[derive(TypeInfo)] 7 | #[scale_info(bounds(T: Default + TypeInfo + 'static, N: TypeInfo + 'static))] 8 | struct Hey { 9 | ciao: Greet, 10 | ho: N, 11 | } 12 | 13 | #[derive(TypeInfo)] 14 | #[scale_info(bounds(T: TypeInfo + 'static))] 15 | struct Greet { 16 | marker: PhantomData, 17 | } 18 | 19 | #[derive(TypeInfo, Default)] 20 | struct SomeType; 21 | 22 | fn assert_type_info() {} 23 | 24 | fn main() { 25 | assert_type_info::>(); 26 | } -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_custom_bounds_empty.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | use core::marker::PhantomData; 4 | 5 | #[derive(TypeInfo)] 6 | #[scale_info(bounds(), skip_type_params(T))] 7 | struct A { 8 | marker: PhantomData, 9 | } 10 | 11 | fn main() {} -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_custom_bounds_fix_overflow.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[allow(unused)] 5 | #[derive(TypeInfo)] 6 | // Without this we get `overflow evaluating the requirement `Vec>: TypeInfo``. 7 | // The custom bounds replace the auto generated bounds. 8 | #[scale_info(bounds(T: TypeInfo + 'static))] 9 | struct A { 10 | a: Vec>, 11 | b: Vec>, 12 | marker: core::marker::PhantomData, 13 | } 14 | 15 | #[allow(unused)] 16 | #[derive(TypeInfo)] 17 | struct B(A); 18 | 19 | fn assert_type_info() {} 20 | 21 | fn main() { 22 | assert_type_info::>(); 23 | } -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_no_implicit_prelude.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | 3 | use ::info::{self as scale_info}; 4 | use scale_info::TypeInfo; 5 | 6 | #[allow(dead_code)] 7 | #[derive(TypeInfo)] 8 | struct S { a: bool } 9 | 10 | fn assert_type_info() {} 11 | 12 | fn main() { 13 | assert_type_info::(); 14 | } 15 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_non_static_lifetime.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[derive(TypeInfo)] 5 | struct Me<'a> { 6 | _me: &'a Me<'a>, 7 | } 8 | 9 | fn assert_type_info() {} 10 | 11 | fn main() { 12 | assert_type_info::(); 13 | } 14 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_raw_identifers.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[allow(dead_code, non_camel_case_types)] 5 | mod r#mod { 6 | use super::*; 7 | #[derive(TypeInfo)] 8 | pub enum r#enum { 9 | r#true, 10 | } 11 | #[derive(TypeInfo)] 12 | pub struct r#struct { 13 | r#try: r#enum, 14 | } 15 | } 16 | 17 | fn assert_type_info() {} 18 | 19 | fn main() { 20 | assert_type_info::(); 21 | } 22 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_self_referential.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[derive(TypeInfo)] 5 | struct Me { 6 | _me: Box, 7 | } 8 | 9 | fn assert_type_info() {} 10 | 11 | fn main() { 12 | assert_type_info::(); 13 | } 14 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_skip_type_params.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | trait Config { 5 | type Balance; 6 | } 7 | 8 | struct Runtime; 9 | 10 | impl Config for Runtime { 11 | type Balance = u64; 12 | } 13 | 14 | #[allow(unused)] 15 | #[derive(TypeInfo)] 16 | #[scale_info(skip_type_params(T))] 17 | struct A { 18 | balance: T::Balance, 19 | marker: core::marker::PhantomData, 20 | } 21 | 22 | fn assert_type_info() {} 23 | 24 | fn main() { 25 | assert_type_info::>(); 26 | } -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_use_codec_attrs_without_deriving_encode.rs: -------------------------------------------------------------------------------- 1 | use info::{self as scale_info}; 2 | use scale_info::TypeInfo; 3 | 4 | #[derive(TypeInfo)] 5 | struct AttrValidation { 6 | a: u8, 7 | #[codec(skip)] 8 | b: u16, 9 | } 10 | 11 | fn assert_type_info() {} 12 | 13 | fn main() { 14 | assert_type_info::(); 15 | } 16 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_with_custom_crate_path.rs: -------------------------------------------------------------------------------- 1 | // scale_info only exists as "::info", so this test 2 | // helps ensure that we never point to `scale_info::*` 3 | // and only use our renamed crate path. 4 | 5 | use info::TypeInfo; 6 | 7 | #[derive(TypeInfo)] 8 | #[scale_info(crate = info)] 9 | struct MyStruct { 10 | bar: bool 11 | } 12 | 13 | #[derive(TypeInfo)] 14 | #[scale_info(crate = info)] 15 | enum MyEnum { 16 | Variant { s: MyStruct } 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /test_suite/tests/ui/pass_with_valid_codec_attrs.rs: -------------------------------------------------------------------------------- 1 | use info::TypeInfo; 2 | use scale::{Encode, HasCompact}; 3 | 4 | #[derive(TypeInfo, Encode)] 5 | #[scale_info(crate = info)] 6 | struct ValidStruct { 7 | #[codec(skip)] 8 | a: u8, 9 | #[codec(compact)] 10 | b: u16, 11 | #[codec(encoded_as = "::Type")] 12 | c: u32, 13 | } 14 | 15 | #[derive(TypeInfo, Encode)] 16 | #[scale_info(crate = info)] 17 | enum ValidEnum { 18 | #[allow(unused)] 19 | #[codec(index = 3)] 20 | Thing(u32), 21 | #[allow(unused)] 22 | #[codec(skip)] 23 | Thong(bool), 24 | #[allow(unused)] 25 | Theng(ValidStruct), 26 | } 27 | 28 | fn assert_type_info() {} 29 | 30 | fn main() { 31 | assert_type_info::(); 32 | assert_type_info::(); 33 | } 34 | --------------------------------------------------------------------------------