Use scale-typegen as a backend for the codegen (#1260)

* integrate scale-typegen, remove types mod

* reintroduce default substitutes and derives

* support runtime_types only again

* generating polkadot.rs ok

* update scale-typegen to discrete error types

* scale-typegen-api-changes

* add note about UncheckedExtrinsic in default substitutes

* add resursive attributes and derives

* adjust example where Clone bound recursive

* move scale-typegen dependency to workspace

* expose default typegen settings

* lightclient: Fix wasm socket closure called after being dropped (#1289)

* lightclient: Close wasm socket while dropping from connecting state

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Construct one time only closures

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* testing: Enable console logs for lightclient WASM testing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Separate wakes and check connectivity on poll_read

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Close the socket depending on internal state

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Revert "lightclient: Separate wakes and check connectivity on poll_read"

This reverts commit 866094001d4c0b119a80ed681a74b323f74eae1b.

* lightclient: Return pending if socket is opening from poll_read

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Close the socket on `poll_close`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Reset closures on Drop to avoid recursive invokation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Close the socket if not already closing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* workflows: Install rustup component for building substrate (#1295)

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Command to fetch chainSpec and optimise its size (#1278)

* cli: Add chainSpec command

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli/chainSpec: Move to dedicated module

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Compute the state root hash

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Remove code substitutes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* artifacts: Update polkadot.json

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* scripts: Generate the chain spec

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Remove testing artifacts

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Apply rustfmt

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Introduce feature flag for smoldot dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Rename chain-spec to chain-spec-pruning

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* scripts: Update chain-spec command

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* remove comments and unused args

* Update substrate- and signer-related dependencies (#1297)

* update crypto dependencies, adjust keypair

* add scale_info::TypeInfo derive in some places

* add multi signature derive

* fix lock file

* fix lock file again :|

* adjust to new interface in scale-typegen

* use released scale typegen

* reintroduce type aliases

* introduce type aliases again using scale-typegen

* cargo fmt and clippy

* reconcile changes with master branch

* update polkadot.rs

* bump scale-typgen to fix substitution

* implemented Alex suggestions, regenerated polkadot.rs (did not change)

* resolve conflicts in Cargo.lock

* make expect messages more clear

* correct typos

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
Tadeo Hepperle
2024-01-11 16:42:51 +01:00
committed by GitHub
parent d004789b04
commit fc5a18aaa0
30 changed files with 1822 additions and 4874 deletions
Generated
+157 -195
View File
@@ -52,19 +52,19 @@ version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
dependencies = [
"getrandom 0.2.11",
"getrandom 0.2.12",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"getrandom 0.2.11",
"getrandom 0.2.12",
"once_cell",
"version_check",
"zerocopy",
@@ -117,9 +117,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -146,9 +146,9 @@ dependencies = [
[[package]]
name = "anstyle-query"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys 0.52.0",
]
@@ -165,9 +165,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.75"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "ark-bls12-377"
@@ -344,7 +344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c"
dependencies = [
"concurrent-queue",
"event-listener 4.0.0",
"event-listener 4.0.3",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
@@ -356,7 +356,7 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c"
dependencies = [
"async-lock 3.2.0",
"async-lock 3.3.0",
"async-task",
"concurrent-queue",
"fastrand",
@@ -370,25 +370,25 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd1f344136bad34df1f83a47f3fd7f2ab85d75cb8a940af4ccf6d482a84ea01b"
dependencies = [
"async-lock 3.2.0",
"async-lock 3.3.0",
"blocking",
"futures-lite",
]
[[package]]
name = "async-io"
version = "2.2.1"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff"
checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7"
dependencies = [
"async-lock 3.2.0",
"async-lock 3.3.0",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 0.38.26",
"rustix 0.38.28",
"slab",
"tracing",
"windows-sys 0.52.0",
@@ -405,11 +405,11 @@ dependencies = [
[[package]]
name = "async-lock"
version = "3.2.0"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c"
checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b"
dependencies = [
"event-listener 4.0.0",
"event-listener 4.0.3",
"event-listener-strategy",
"pin-project-lite",
]
@@ -433,13 +433,13 @@ checksum = "15c1cd5d253ecac3d3cf15e390fd96bd92a13b1d14497d81abf077304794fb04"
dependencies = [
"async-channel",
"async-io",
"async-lock 3.2.0",
"async-lock 3.3.0",
"async-signal",
"blocking",
"cfg-if",
"event-listener 4.0.0",
"event-listener 4.0.3",
"futures-lite",
"rustix 0.38.26",
"rustix 0.38.28",
"windows-sys 0.52.0",
]
@@ -455,7 +455,7 @@ dependencies = [
"cfg-if",
"futures-core",
"futures-io",
"rustix 0.38.26",
"rustix 0.38.28",
"signal-hook-registry",
"slab",
"windows-sys 0.48.0",
@@ -463,15 +463,15 @@ dependencies = [
[[package]]
name = "async-task"
version = "4.5.0"
version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1"
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-trait"
version = "0.1.74"
version = "0.1.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
@@ -518,7 +518,7 @@ dependencies = [
"cfg-if",
"libc",
"miniz_oxide",
"object 0.32.1",
"object 0.32.2",
"rustc-demangle",
]
@@ -536,9 +536,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.5"
version = "0.21.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9"
[[package]]
name = "base64ct"
@@ -548,9 +548,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "basic-toml"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778"
checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5"
dependencies = [
"serde",
]
@@ -692,7 +692,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
dependencies = [
"async-channel",
"async-lock 3.2.0",
"async-lock 3.3.0",
"async-task",
"fastrand",
"futures-io",
@@ -847,9 +847,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.13"
version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
dependencies = [
"clap_builder",
"clap_derive",
@@ -857,9 +857,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.12"
version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
dependencies = [
"anstream",
"anstyle",
@@ -947,9 +947,9 @@ dependencies = [
[[package]]
name = "const-oid"
version = "0.9.5"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "constant_time_eq"
@@ -1002,9 +1002,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
@@ -1065,46 +1065,37 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset 0.9.0",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.8"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
@@ -1301,12 +1292,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.8.1"
@@ -1458,9 +1443,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "event-listener"
version = "4.0.0"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae"
checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
dependencies = [
"concurrent-queue",
"parking",
@@ -1473,15 +1458,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
dependencies = [
"event-listener 4.0.0",
"event-listener 4.0.3",
"pin-project-lite",
]
[[package]]
name = "eyre"
version = "0.6.9"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd"
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
dependencies = [
"indenter",
"once_cell",
@@ -1618,9 +1603,9 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-lite"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143"
checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba"
dependencies = [
"fastrand",
"futures-core",
@@ -1721,9 +1706,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"js-sys",
@@ -1872,7 +1857,7 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.6",
"ahash 0.8.7",
]
[[package]]
@@ -1881,7 +1866,7 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash 0.8.6",
"ahash 0.8.7",
"allocator-api2",
"serde",
]
@@ -1955,11 +1940,11 @@ dependencies = [
[[package]]
name = "home"
version = "0.5.5"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1975,9 +1960,9 @@ dependencies = [
[[package]]
name = "http-body"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http",
@@ -1998,9 +1983,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.27"
version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [
"bytes",
"futures-channel",
@@ -2013,7 +1998,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2 0.4.10",
"socket2",
"tokio",
"tower-service",
"tracing",
@@ -2038,9 +2023,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.58"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -2224,9 +2209,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "js-sys"
@@ -2345,9 +2330,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libm"
@@ -2460,9 +2445,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.6.4"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memfd"
@@ -2470,7 +2455,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
dependencies = [
"rustix 0.38.26",
"rustix 0.38.28",
]
[[package]]
@@ -2482,15 +2467,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "memory-db"
version = "0.32.0"
@@ -2664,18 +2640,18 @@ dependencies = [
[[package]]
name = "object"
version = "0.32.1"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
@@ -2860,9 +2836,9 @@ dependencies = [
[[package]]
name = "platforms"
version = "3.2.0"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
[[package]]
name = "plotters"
@@ -2901,7 +2877,7 @@ dependencies = [
"cfg-if",
"concurrent-queue",
"pin-project-lite",
"rustix 0.38.26",
"rustix 0.38.28",
"tracing",
"windows-sys 0.52.0",
]
@@ -2923,16 +2899,6 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty_assertions"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "primitive-types"
version = "0.12.2"
@@ -3082,7 +3048,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.11",
"getrandom 0.2.12",
]
[[package]]
@@ -3125,18 +3091,18 @@ dependencies = [
[[package]]
name = "ref-cast"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280"
checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925"
checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
dependencies = [
"proc-macro2",
"quote",
@@ -3194,7 +3160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom 0.2.11",
"getrandom 0.2.12",
"libc",
"spin",
"untrusted",
@@ -3244,9 +3210,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.26"
version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
"bitflags 2.4.1",
"errno",
@@ -3257,9 +3223,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.9"
version = "0.21.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [
"log",
"ring",
@@ -3285,7 +3251,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.5",
"base64 0.21.6",
]
[[package]]
@@ -3317,9 +3283,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "same-file"
@@ -3423,6 +3389,19 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "scale-typegen"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00860983481ac590ac87972062909bef0d6a658013b592ccc0f2feb272feab11"
dependencies = [
"proc-macro2",
"quote",
"scale-info",
"syn 2.0.48",
"thiserror",
]
[[package]]
name = "scale-value"
version = "0.13.0"
@@ -3445,11 +3424,11 @@ dependencies = [
[[package]]
name = "schannel"
version = "0.1.22"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -3458,7 +3437,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d"
dependencies = [
"ahash 0.8.6",
"ahash 0.8.7",
"cfg-if",
"hashbrown 0.13.2",
]
@@ -3586,9 +3565,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "send_wrapper"
@@ -3613,9 +3592,9 @@ dependencies = [
[[package]]
name = "serde_bytes"
version = "0.11.12"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
dependencies = [
"serde",
]
@@ -3633,9 +3612,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.108"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
@@ -3759,7 +3738,7 @@ dependencies = [
"async-executor",
"async-fs",
"async-io",
"async-lock 3.2.0",
"async-lock 3.3.0",
"async-net",
"async-process",
"blocking",
@@ -3773,9 +3752,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d1eaa97d77be4d026a1e7ffad1bb3b78448763b357ea6f8188d3e6f736a9b9"
dependencies = [
"arrayvec 0.7.4",
"async-lock 3.2.0",
"async-lock 3.3.0",
"atomic-take",
"base64 0.21.5",
"base64 0.21.6",
"bip39",
"blake2-rfc",
"bs58",
@@ -3784,7 +3763,7 @@ dependencies = [
"derive_more",
"ed25519-zebra 4.0.3",
"either",
"event-listener 4.0.0",
"event-listener 4.0.3",
"fnv",
"futures-lite",
"futures-util",
@@ -3828,12 +3807,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5496f2d116b7019a526b1039ec2247dd172b8670633b1a64a614c9ea12c9d8c7"
dependencies = [
"async-channel",
"async-lock 3.2.0",
"base64 0.21.5",
"async-lock 3.3.0",
"base64 0.21.6",
"blake2-rfc",
"derive_more",
"either",
"event-listener 4.0.0",
"event-listener 4.0.3",
"fnv",
"futures-channel",
"futures-lite",
@@ -3857,16 +3836,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "socket2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "socket2"
version = "0.5.5"
@@ -4183,7 +4152,7 @@ version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c4bf89a5bd74f696cd1f23d83bb6abe6bd0abad1f3c70d4b0d7ebec4098cfe"
dependencies = [
"ahash 0.8.6",
"ahash 0.8.7",
"hash-db",
"hashbrown 0.13.2",
"lazy_static",
@@ -4250,9 +4219,9 @@ dependencies = [
[[package]]
name = "ss58-registry"
version = "1.44.0"
version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35935738370302d5e33963665b77541e4b990a3e919ec904c837a56cfc891de1"
checksum = "3c0c74081753a8ce1c8eb10b9f262ab6f7017e5ad3317c17a54c7ab65fcb3c6e"
dependencies = [
"Inflector",
"num-format",
@@ -4339,7 +4308,7 @@ dependencies = [
"either",
"frame-metadata 16.0.0",
"futures",
"getrandom 0.2.11",
"getrandom 0.2.12",
"hex",
"impl-serde",
"jsonrpsee",
@@ -4372,7 +4341,7 @@ dependencies = [
name = "subxt-cli"
version = "0.33.0"
dependencies = [
"clap 4.4.13",
"clap 4.4.14",
"color-eyre",
"frame-metadata 16.0.0",
"hex",
@@ -4395,17 +4364,16 @@ dependencies = [
name = "subxt-codegen"
version = "0.33.0"
dependencies = [
"bitvec",
"frame-metadata 16.0.0",
"getrandom 0.2.11",
"getrandom 0.2.12",
"heck",
"hex",
"jsonrpsee",
"parity-scale-codec",
"pretty_assertions",
"proc-macro2",
"quote",
"scale-info",
"scale-typegen",
"subxt-metadata",
"syn 2.0.48",
"thiserror",
@@ -4420,7 +4388,7 @@ dependencies = [
"futures",
"futures-timer",
"futures-util",
"getrandom 0.2.11",
"getrandom 0.2.12",
"instant",
"js-sys",
"pin-project",
@@ -4468,7 +4436,7 @@ name = "subxt-signer"
version = "0.33.0"
dependencies = [
"bip39",
"getrandom 0.2.11",
"getrandom 0.2.12",
"hex",
"hmac 0.12.1",
"parity-scale-codec",
@@ -4516,9 +4484,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
version = "0.12.12"
version = "0.12.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
[[package]]
name = "termcolor"
@@ -4552,18 +4520,18 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.53"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.53"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
@@ -4617,7 +4585,7 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
"socket2 0.5.5",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
@@ -4859,15 +4827,15 @@ dependencies = [
[[package]]
name = "try-lock"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "trybuild"
version = "1.0.86"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e"
checksum = "76de4f783e610194f6c98bfd53f9fc52bb2e0d02c947621e8a0f4ecc799b2880"
dependencies = [
"basic-toml",
"glob 0.3.1",
@@ -4924,9 +4892,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@@ -5298,7 +5266,7 @@ dependencies = [
"log",
"mach",
"memfd",
"memoffset 0.8.0",
"memoffset",
"paste",
"rand 0.8.5",
"rustix 0.36.17",
@@ -5339,7 +5307,7 @@ dependencies = [
"either",
"home",
"once_cell",
"rustix 0.38.26",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@@ -5376,11 +5344,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.51.1"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.48.5",
"windows-targets 0.52.0",
]
[[package]]
@@ -5583,9 +5551,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.25"
version = "0.5.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e87b8dfbe3baffbe687eef2e164e32286eff31a5ee16463ce03d991643ec94"
checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa"
dependencies = [
"memchr",
]
@@ -5611,12 +5579,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yap"
version = "0.11.0"
+3 -7
View File
@@ -17,13 +17,7 @@ members = [
# We exclude any crates that would depend on non mutually
# exclusive feature flags and thus can't compile with the
# workspace:
exclude = [
"testing/wasm-rpc-tests",
"testing/wasm-lightclient-tests",
"signer/wasm-tests",
"examples/wasm-example",
"examples/parachain-example"
]
exclude = ["testing/wasm-rpc-tests", "testing/wasm-lightclient-tests", "signer/wasm-tests", "examples/wasm-example", "examples/parachain-example"]
resolver = "2"
[workspace.package]
@@ -100,6 +94,8 @@ url = "2.5.0"
wabt = "0.10.0"
wasm-bindgen-test = "0.3.24"
which = "5.0.0"
scale-typegen-description = "0.1.0"
scale-typegen = "0.1.1"
# Light client support:
smoldot = { version = "0.16.0", default-features = false }
+4 -2
View File
@@ -173,7 +173,8 @@ fn codegen(
.map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?;
let derive = syn::parse_str(&derive)
.map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?;
codegen.add_derives_for_type(ty, std::iter::once(derive));
// Note: recursive derives and attributes not supported in the CLI => recursive: false
codegen.add_derives_for_type(ty, std::iter::once(derive), false);
}
// Configure attribtues:
@@ -190,7 +191,8 @@ fn codegen(
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
let attribute: OuterAttribute = syn::parse_str(&attr)
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
codegen.add_attributes_for_type(ty, std::iter::once(attribute.0));
// Note: recursive derives and attributes not supported in the CLI => recursive: false
codegen.add_attributes_for_type(ty, std::iter::once(attribute.0), false);
}
// Insert type substitutions:
+10 -10
View File
@@ -228,11 +228,11 @@ impl StorageEntryDiff {
let value_1_ty_id = storage_entry_1.entry_type().value_ty();
let value_1_hash = metadata_1
.type_hash(value_1_ty_id)
.expect("type should be present");
.expect("type is in metadata; qed");
let value_2_ty_id = storage_entry_2.entry_type().value_ty();
let value_2_hash = metadata_1
.type_hash(value_2_ty_id)
.expect("type should be present");
.expect("type is in metadata; qed");
let value_different = value_1_hash != value_2_hash;
let key_1_hash = storage_entry_1
@@ -241,7 +241,7 @@ impl StorageEntryDiff {
.map(|key_ty| {
metadata_1
.type_hash(key_ty)
.expect("type should be present")
.expect("type is in metadata; qed")
})
.unwrap_or_default();
let key_2_hash = storage_entry_2
@@ -250,7 +250,7 @@ impl StorageEntryDiff {
.map(|key_ty| {
metadata_2
.type_hash(key_ty)
.expect("type should be present")
.expect("type is in metadata; qed")
})
.unwrap_or_default();
let key_different = key_1_hash != key_2_hash;
@@ -309,12 +309,12 @@ fn storage_differences<'a>(
|e| {
pallet_metadata_1
.storage_hash(e.name())
.expect("storage entry should be present")
.expect("storage entry is in metadata; qed")
},
|e| {
pallet_metadata_2
.storage_hash(e.name())
.expect("storage entry should be present")
.expect("storage entry is in metadata; qed")
},
|e| e.name(),
)
@@ -330,12 +330,12 @@ fn calls_differences<'a>(
|e| {
pallet_metadata_1
.call_hash(&e.name)
.expect("call should be present")
.expect("call is in metadata; qed")
},
|e| {
pallet_metadata_2
.call_hash(&e.name)
.expect("call should be present")
.expect("call is in metadata; qed")
},
|e| &e.name,
);
@@ -351,12 +351,12 @@ fn constants_differences<'a>(
|e| {
pallet_metadata_1
.constant_hash(e.name())
.expect("constant should be present")
.expect("constant is in metadata; qed")
},
|e| {
pallet_metadata_2
.constant_hash(e.name())
.expect("constant should be present")
.expect("constant is in metadata; qed")
},
|e| e.name(),
)
+1 -1
View File
@@ -142,7 +142,7 @@ pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Re
explore_constants(command, &metadata, pallet_metadata, output)
}
PalletSubcommand::Storage(command) => {
// if the metadata came from some url, we use that same url to make storage calls against.
// if the metadata is in some url, we use that same url to make storage calls against.
let node_url = opts.file_or_url.url.map(|url| url.to_string());
explore_storage(command, &metadata, pallet_metadata, node_url, output).await
}
+1 -3
View File
@@ -31,12 +31,10 @@ jsonrpsee = { workspace = true, features = ["async-client", "client-ws-transport
hex = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread"], optional = true }
thiserror = { workspace = true }
scale-typegen = { workspace = true }
# Included if "web" feature is enabled, to enable its js feature.
getrandom = { workspace = true, optional = true }
[dev-dependencies]
bitvec = { workspace = true }
scale-info = { workspace = true, features = ["bit-vec"] }
pretty_assertions = { workspace = true }
frame-metadata = { workspace = true }
+31 -45
View File
@@ -3,10 +3,10 @@
// see LICENSE for license details.
use super::CodegenError;
use crate::types::{CompositeDefFields, TypeGenerator};
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use scale_typegen::{typegen::ir::type_ir::CompositeIRKind, TypeGenerator};
use subxt_metadata::PalletMetadata;
/// Generate calls from the provided pallet's metadata. Each call returns a `StaticTxPayload`
@@ -14,81 +14,69 @@ use subxt_metadata::PalletMetadata;
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the calls are generated.
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the calls are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
pub fn generate_calls(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no calls.
let Some(call_ty) = pallet.call_ty_id() else {
return Ok(quote!());
};
let mut struct_defs = super::generate_structs_from_variants(
let variant_names_and_struct_defs = super::generate_structs_from_variants(
type_gen,
types_mod_ident,
call_ty,
|name| name.to_upper_camel_case().into(),
"Call",
crate_path,
should_gen_docs,
)?;
let result = struct_defs
.iter_mut()
.map(|(variant_name, struct_def, aliases)| {
let fn_name = format_ident!("{}", variant_name.to_snake_case());
let result: Vec<_> = match struct_def.fields {
CompositeDefFields::Named(ref named_fields) => named_fields
let (call_structs, call_fns): (Vec<_>, Vec<_>) = variant_names_and_struct_defs
.into_iter()
.map(|var| {
let (call_fn_args, call_args): (Vec<_>, Vec<_>) = match &var.composite.kind {
CompositeIRKind::Named(named_fields) => named_fields
.iter()
.map(|(name, field)| {
let call_arg = if field.is_boxed() {
// Note: fn_arg_type this is relative the type path of the type alias when prefixed with `types::`, e.g. `set_max_code_size::New`
let fn_arg_type = &field.type_path;
let call_arg = if field.is_boxed {
quote! { #name: ::std::boxed::Box::new(#name) }
} else {
quote! { #name }
};
let alias_name =
format_ident!("{}", name.to_string().to_upper_camel_case());
(quote!( #name: types::#fn_name::#alias_name ), call_arg)
(quote!( #name: types::#fn_arg_type ), call_arg)
})
.collect(),
CompositeDefFields::NoFields => Default::default(),
CompositeDefFields::Unnamed(_) => {
.unzip(),
CompositeIRKind::NoFields => Default::default(),
CompositeIRKind::Unnamed(_) => {
return Err(CodegenError::InvalidCallVariant(call_ty))
}
};
let call_fn_args = result.iter().map(|(call_fn_arg, _)| call_fn_arg);
let call_args = result.iter().map(|(_, call_arg)| call_arg);
let pallet_name = pallet.name();
let call_name = &variant_name;
let struct_name = &struct_def.name;
let call_name = &var.variant_name;
let struct_name = &var.composite.name;
let Some(call_hash) = pallet.call_hash(call_name) else {
return Err(CodegenError::MissingCallMetadata(
pallet_name.into(),
call_name.to_string(),
));
};
let fn_name = format_ident!("{}", var.variant_name.to_snake_case());
// Propagate the documentation just to `TransactionApi` methods, while
// draining the documentation of inner call structures.
let docs = should_gen_docs.then_some(struct_def.docs.take()).flatten();
let docs = &var.composite.docs;
// this converts the composite into a full struct type. No Type Parameters needed here.
let struct_def = type_gen.upcast_composite(&var.composite);
let alias_mod = var.type_alias_mod;
// The call structure's documentation was stripped above.
let call_struct = quote! {
#struct_def
#aliases
#alias_mod
impl #crate_path::blocks::StaticExtrinsic for #struct_name {
const PALLET: &'static str = #pallet_name;
@@ -113,17 +101,15 @@ pub fn generate_calls(
Ok((call_struct, client_fn))
})
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip();
let call_structs = result.iter().map(|(call_struct, _)| call_struct);
let call_fns = result.iter().map(|(_, client_fn)| client_fn);
let call_type = type_gen.resolve_type_path(call_ty)?;
let call_ty = type_gen.resolve_type(call_ty)?;
let docs = type_gen.docs_from_scale_info(&call_ty.docs);
let call_type = type_gen.resolve_type_path(call_ty);
let call_ty = type_gen.resolve_type(call_ty);
let docs = &call_ty.docs;
let docs = should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
let types_mod_ident = type_gen.types_mod_ident();
Ok(quote! {
#docs
+10 -9
View File
@@ -2,10 +2,10 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::types::TypeGenerator;
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
use super::CodegenError;
@@ -29,16 +29,13 @@ use super::CodegenError;
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the calls are generated.
/// - `type_gen` - The type generator containing all types defined by metadata
/// - `pallet` - Pallet metadata from which the calls are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the constants are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
pub fn generate_constants(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no constants.
if pallet.constants().len() == 0 {
@@ -58,9 +55,11 @@ pub fn generate_constants(
));
};
let return_ty = type_gen.resolve_type_path(constant.ty());
let return_ty = type_gen.resolve_type_path(constant.ty())?;
let docs = constant.docs();
let docs = should_gen_docs
let docs = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
@@ -77,6 +76,8 @@ pub fn generate_constants(
})
.collect::<Result<Vec<_>, _>>()?;
let types_mod_ident = type_gen.types_mod_ident();
Ok(quote! {
pub mod constants {
use super::#types_mod_ident;
+3 -3
View File
@@ -2,10 +2,9 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::collections::HashSet;
use crate::types::TypeGenerator;
use heck::ToSnakeCase as _;
use scale_typegen::TypeGenerator;
use std::collections::HashSet;
use subxt_metadata::{CustomValueMetadata, Metadata};
use proc_macro2::TokenStream as TokenStream2;
@@ -60,6 +59,7 @@ fn generate_custom_value_fn(
let (return_ty, decodable) = if type_is_valid {
let return_ty = type_gen
.resolve_type_path(custom_value.type_id())
.expect("type is in metadata; qed")
.to_token_stream();
let decodable = quote!(#crate_path::custom_values::Yes);
(return_ty, decodable)
+6 -6
View File
@@ -4,26 +4,26 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
use crate::types::TypeGenerator;
use super::CodegenError;
/// Generate error type alias from the provided pallet metadata.
pub fn generate_error_type_alias(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let Some(error_ty) = pallet.error_ty_id() else {
return Ok(quote!());
};
let error_type = type_gen.resolve_type_path(error_ty);
let error_ty = type_gen.resolve_type(error_ty);
let error_type = type_gen.resolve_type_path(error_ty)?;
let error_ty = type_gen.resolve_type(error_ty)?;
let docs = &error_ty.docs;
let docs = should_gen_docs
let docs = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
Ok(quote! {
+26 -33
View File
@@ -2,9 +2,9 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::types::TypeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
use super::CodegenError;
@@ -35,55 +35,48 @@ use super::CodegenError;
///
/// # Arguments
///
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the events are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
pub fn generate_events(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no events.
let Some(event_ty) = pallet.event_ty_id() else {
return Ok(quote!());
};
let struct_defs = super::generate_structs_from_variants(
type_gen,
types_mod_ident,
event_ty,
|name| name.into(),
"Event",
crate_path,
should_gen_docs,
)?;
let variant_names_and_struct_defs =
super::generate_structs_from_variants(type_gen, event_ty, |name| name.into(), "Event")?;
let event_structs = struct_defs
.iter()
.map(|(variant_name, struct_def, aliases)| {
let pallet_name = pallet.name();
let event_struct = &struct_def.name;
let event_name = variant_name;
let event_structs = variant_names_and_struct_defs.into_iter().map(|var| {
let pallet_name = pallet.name();
let event_struct_name = &var.composite.name;
let event_name = var.variant_name;
let alias_mod = var.type_alias_mod;
let struct_def = type_gen.upcast_composite(&var.composite);
quote! {
#struct_def
#alias_mod
quote! {
#struct_def
#aliases
impl #crate_path::events::StaticEvent for #event_struct {
const PALLET: &'static str = #pallet_name;
const EVENT: &'static str = #event_name;
}
impl #crate_path::events::StaticEvent for #event_struct_name {
const PALLET: &'static str = #pallet_name;
const EVENT: &'static str = #event_name;
}
});
let event_type = type_gen.resolve_type_path(event_ty);
let event_ty = type_gen.resolve_type(event_ty);
}
});
let event_type = type_gen.resolve_type_path(event_ty)?;
let event_ty = type_gen.resolve_type(event_ty)?;
let docs = &event_ty.docs;
let docs = should_gen_docs
let docs = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
let types_mod_ident = type_gen.types_mod_ident();
Ok(quote! {
#docs
+111 -140
View File
@@ -12,20 +12,20 @@ mod events;
mod runtime_apis;
mod storage;
use scale_typegen::typegen::ir::type_ir::{CompositeFieldIR, CompositeIR, CompositeIRKind};
use scale_typegen::typegen::type_params::TypeParameters;
use scale_typegen::typegen::type_path::TypePath;
use scale_typegen::TypeGenerator;
use subxt_metadata::Metadata;
use syn::{parse_quote, Ident};
use crate::api::custom_values::generate_custom_values;
use crate::error::CodegenError;
use crate::types::DerivesRegistry;
use crate::{
ir,
types::{CompositeDef, CompositeDefFields, TypeGenerator, TypeSubstitutes},
};
use crate::subxt_type_gen_settings;
use crate::{api::custom_values::generate_custom_values, ir};
use heck::ToSnakeCase as _;
use heck::{ToSnakeCase as _, ToUpperCamelCase};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::parse_quote;
/// Create the API for interacting with a Substrate runtime.
pub struct RuntimeGenerator {
@@ -60,27 +60,22 @@ impl RuntimeGenerator {
pub fn generate_runtime_types(
&self,
item_mod: syn::ItemMod,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
derives: scale_typegen::DerivesRegistry,
type_substitutes: scale_typegen::TypeSubstitutes,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let item_mod_attrs = item_mod.attrs.clone();
let item_mod_ir = ir::ItemMod::try_from(item_mod)?;
let settings =
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
let types_mod = type_gen.generate_types_mod()?;
let mod_ident = &item_mod_ir.ident;
let rust_items = item_mod_ir.rust_items();
let type_gen = TypeGenerator::new(
self.metadata.types(),
"runtime_types",
type_substitutes,
derives,
crate_path,
should_gen_docs,
);
let types_mod = type_gen.generate_types_mod()?;
Ok(quote! {
#( #item_mod_attrs )*
#[allow(dead_code, unused_imports, non_camel_case_types)]
@@ -114,24 +109,20 @@ impl RuntimeGenerator {
pub fn generate_runtime(
&self,
item_mod: syn::ItemMod,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
derives: scale_typegen::DerivesRegistry,
type_substitutes: scale_typegen::TypeSubstitutes,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let item_mod_attrs = item_mod.attrs.clone();
let item_mod_ir = ir::ItemMod::try_from(item_mod)?;
let type_gen = TypeGenerator::new(
self.metadata.types(),
"runtime_types",
type_substitutes,
derives,
crate_path.clone(),
should_gen_docs,
);
let settings =
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
let types_mod = type_gen.generate_types_mod()?;
let types_mod_ident = types_mod.ident();
let types_mod_ident = type_gen.types_mod_ident();
let pallets_with_mod_names = self
.metadata
.pallets()
@@ -165,39 +156,15 @@ impl RuntimeGenerator {
let modules = pallets_with_mod_names
.iter()
.map(|(pallet, mod_name)| {
let calls = calls::generate_calls(
&type_gen,
pallet,
types_mod_ident,
&crate_path,
should_gen_docs,
)?;
let calls = calls::generate_calls(&type_gen, pallet, &crate_path)?;
let event = events::generate_events(
&type_gen,
pallet,
types_mod_ident,
&crate_path,
should_gen_docs,
)?;
let event = events::generate_events(&type_gen, pallet, &crate_path)?;
let storage_mod = storage::generate_storage(
&type_gen,
pallet,
types_mod_ident,
&crate_path,
should_gen_docs,
)?;
let storage_mod = storage::generate_storage(&type_gen, pallet, &crate_path)?;
let constants_mod = constants::generate_constants(
&type_gen,
pallet,
types_mod_ident,
&crate_path,
should_gen_docs,
)?;
let constants_mod = constants::generate_constants(&type_gen, pallet, &crate_path)?;
let errors = errors::generate_error_type_alias(&type_gen, pallet, should_gen_docs)?;
let errors = errors::generate_error_type_alias(&type_gen, pallet)?;
Ok(quote! {
pub mod #mod_name {
@@ -242,14 +209,14 @@ impl RuntimeGenerator {
&type_gen,
types_mod_ident,
&crate_path,
should_gen_docs,
)?;
// Fetch the paths of the outer enums.
// Substrate exposes those under `kitchensink_runtime`, while Polkadot under `polkadot_runtime`.
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty());
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty());
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty());
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty())?;
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty())?;
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?;
let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);
@@ -358,17 +325,14 @@ impl RuntimeGenerator {
/// Return a vector of tuples of variant names and corresponding struct definitions.
pub fn generate_structs_from_variants<F>(
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
type_id: u32,
variant_to_struct_name: F,
error_message_type_name: &str,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<Vec<(String, CompositeDef, TypeAliases)>, CodegenError>
) -> Result<Vec<StructFromVariant>, CodegenError>
where
F: Fn(&str) -> std::borrow::Cow<str>,
{
let ty = type_gen.resolve_type(type_id);
let ty = type_gen.resolve_type(type_id)?;
let scale_info::TypeDef::Variant(variant) = &ty.type_def else {
return Err(CodegenError::InvalidType(error_message_type_name.into()));
@@ -378,84 +342,91 @@ where
.variants
.iter()
.map(|var| {
let mut type_params = TypeParameters::from_scale_info(&[]);
let composite_ir_kind =
type_gen.create_composite_ir_kind(&var.fields, &mut type_params)?;
let struct_name = variant_to_struct_name(&var.name);
let mut composite = CompositeIR::new(
syn::parse_str(&struct_name).expect("enum variant is a valid ident; qed"),
composite_ir_kind,
type_gen.docs_from_scale_info(&var.docs),
);
let fields = CompositeDefFields::from_scale_info_fields(
struct_name.as_ref(),
&var.fields,
&[],
type_gen,
)?;
let alias_module_name = format_ident!("{}", var.name.to_snake_case());
let docs = should_gen_docs.then_some(&*var.docs).unwrap_or_default();
let struct_def = CompositeDef::struct_def(
&ty,
struct_name.as_ref(),
Default::default(),
fields.clone(),
Some(parse_quote!(pub)),
type_gen,
docs,
crate_path,
Some(alias_module_name.clone()),
)?;
let type_aliases = TypeAliases::new(fields, types_mod_ident.clone(), alias_module_name);
Ok((var.name.to_string(), struct_def, type_aliases))
let type_alias_mod = generate_type_alias_mod(&mut composite, type_gen);
Ok(StructFromVariant {
variant_name: var.name.to_string(),
composite,
type_alias_mod,
})
})
.collect()
}
/// Generate the type aliases from a set of enum / struct definitions.
pub struct StructFromVariant {
variant_name: String,
composite: CompositeIR,
type_alias_mod: TokenStream2,
}
/// Modifies the composite, by replacing its types with references to the generated type alias module.
/// Returns the TokenStream of the type alias module.
///
/// The type aliases are used to make the generated code more readable.
#[derive(Debug)]
pub struct TypeAliases {
fields: CompositeDefFields,
types_mod_ident: syn::Ident,
mod_name: syn::Ident,
}
/// E.g a struct like this:
/// ```ignore
/// pub struct SetMaxCodeSize {
/// pub new: ::core::primitive::u32,
/// }
/// ```
/// will be made into this:
/// ```ignore
/// pub struct SetMaxCodeSize {
/// pub new: set_max_code_size::New,
/// }
/// ```
/// And the type alias module will look like this:
/// ```ignore
/// pub mod set_max_code_size {
/// use super::runtime_types;
/// pub type New = ::core::primitive::u32;
/// }
/// ```
pub fn generate_type_alias_mod(
composite: &mut CompositeIR,
type_gen: &TypeGenerator,
) -> TokenStream2 {
let mut aliases: Vec<TokenStream2> = vec![];
let alias_mod_name: Ident = syn::parse_str(&composite.name.to_string().to_snake_case())
.expect("composite name in snake_case should be a valid identifier");
impl TypeAliases {
pub fn new(
fields: CompositeDefFields,
types_mod_ident: syn::Ident,
mod_name: syn::Ident,
) -> Self {
TypeAliases {
fields,
types_mod_ident,
mod_name,
let mut modify_field_to_be_type_alias = |field: &mut CompositeFieldIR, alias_name: Ident| {
let type_path = &field.type_path;
aliases.push(quote!(pub type #alias_name = #type_path;));
let type_alias_path: syn::Path = parse_quote!(#alias_mod_name::#alias_name);
field.type_path = TypePath::from_syn_path(type_alias_path);
};
match &mut composite.kind {
CompositeIRKind::NoFields => {
return quote!(); // no types mod generated for unit structs.
}
}
}
impl quote::ToTokens for TypeAliases {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let has_fields = matches!(&self.fields, CompositeDefFields::Named(fields) if !fields.is_empty())
|| matches!(&self.fields, CompositeDefFields::Unnamed(fields) if !fields.is_empty());
if !has_fields {
return;
}
let visibility: syn::Visibility = parse_quote!(pub);
let aliases = self
.fields
.to_type_aliases_tokens(Some(visibility).as_ref());
let mod_name = &self.mod_name;
let types_mod_ident = &self.types_mod_ident;
tokens.extend(quote! {
pub mod #mod_name {
use super::#types_mod_ident;
#aliases
CompositeIRKind::Named(named) => {
for (name, field) in named.iter_mut() {
let alias_name = format_ident!("{}", name.to_string().to_upper_camel_case());
modify_field_to_be_type_alias(field, alias_name);
}
})
}
}
CompositeIRKind::Unnamed(unnamed) => {
for (i, field) in unnamed.iter_mut().enumerate() {
let alias_name = format_ident!("Field{}", i);
modify_field_to_be_type_alias(field, alias_name);
}
}
};
let types_mod_ident = type_gen.types_mod_ident();
quote!(pub mod #alias_mod_name {
use super::#types_mod_ident;
#( #aliases )*
})
}
+107 -95
View File
@@ -4,140 +4,155 @@
use std::collections::HashSet;
use crate::{types::TypeGenerator, CodegenError};
use heck::ToSnakeCase as _;
use heck::ToUpperCamelCase as _;
use scale_typegen::TypeGenerator;
use subxt_metadata::{Metadata, RuntimeApiMetadata};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::CodegenError;
/// Generates runtime functions for the given API metadata.
fn generate_runtime_api(
api: RuntimeApiMetadata,
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
// Trait name must remain as is (upper case) to identity the runtime call.
let trait_name_str = api.name();
// The snake case for the trait name.
let trait_name_snake = format_ident!("{}", api.name().to_snake_case());
let docs = api.docs();
let docs: TokenStream2 = should_gen_docs
let docs: TokenStream2 = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
let structs_and_methods: Vec<_> = api.methods().map(|method| {
let method_name = format_ident!("{}", method.name());
let method_name_str = method.name();
let structs_and_methods: Vec<_> = api
.methods()
.map(|method| {
let method_name = format_ident!("{}", method.name());
let method_name_str = method.name();
let docs = method.docs();
let docs: TokenStream2 = should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
let docs = method.docs();
let docs: TokenStream2 = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
let mut unique_names = HashSet::new();
let mut unique_aliases = HashSet::new();
let mut unique_names = HashSet::new();
let mut unique_aliases = HashSet::new();
let inputs: Vec<_> = method.inputs().enumerate().map(|(idx, input)| {
// These are method names, which can just be '_', but struct field names can't
// just be an underscore, so fix any such names we find to work in structs.
let inputs: Vec<_> = method
.inputs()
.enumerate()
.map(|(idx, input)| {
// These are method names, which can just be '_', but struct field names can't
// just be an underscore, so fix any such names we find to work in structs.
let mut name = input.name.trim_start_matches('_').to_string();
if name.is_empty() {
name = format!("_{}", idx);
}
while !unique_names.insert(name.clone()) {
// Name is already used, append the index until it is unique.
name = format!("{}_param{}", name, idx);
}
let mut name = input.name.trim_start_matches('_').to_string();
if name.is_empty() {
name = format!("_{}", idx);
}
while !unique_names.insert(name.clone()) {
// Name is already used, append the index until it is unique.
name = format!("{}_param{}", name, idx);
}
let mut alias = name.to_upper_camel_case();
// Note: name is not empty.
if alias.as_bytes()[0].is_ascii_digit() {
alias = format!("Param{}", alias);
}
while !unique_aliases.insert(alias.clone()) {
alias = format!("{}Param{}", alias, idx);
}
let mut alias = name.to_upper_camel_case();
// Note: name is not empty.
if alias.as_bytes()[0].is_ascii_digit() {
alias = format!("Param{}", alias);
}
while !unique_aliases.insert(alias.clone()) {
alias = format!("{}Param{}", alias, idx);
}
let (alias_name, name) = (format_ident!("{alias}"), format_ident!("{name}"));
let (alias_name, name) = (format_ident!("{alias}"), format_ident!("{name}"));
// Generate alias for runtime type.
let ty = type_gen
.resolve_type_path(input.ty)
.expect("runtime api input type is in metadata; qed");
let aliased_param = quote!( pub type #alias_name = #ty; );
// Generate alias for runtime type.
let ty = type_gen.resolve_type_path(input.ty);
let aliased_param = quote!( pub type #alias_name = #ty; );
// Structures are placed on the same level as the alias module.
let struct_ty_path = quote!( #method_name::#alias_name );
let struct_param = quote!(#name: #struct_ty_path);
// Structures are placed on the same level as the alias module.
let struct_ty_path = quote!( #method_name::#alias_name );
let struct_param = quote!(#name: #struct_ty_path);
// Function parameters must be indented by `types`.
let fn_param = quote!(#name: types::#struct_ty_path);
(fn_param, struct_param, name, aliased_param)
})
.collect();
// Function parameters must be indented by `types`.
let fn_param = quote!(#name: types::#struct_ty_path);
(fn_param, struct_param, name, aliased_param)
}).collect();
let fn_params = inputs.iter().map(|(fn_param, _, _, _)| fn_param);
let struct_params = inputs.iter().map(|(_, struct_param, _, _)| struct_param);
let param_names = inputs.iter().map(|(_, _, name, _)| name);
let type_aliases = inputs.iter().map(|(_, _, _, aliased_param)| aliased_param);
let types_mod_ident = type_gen.types_mod_ident();
let fn_params = inputs.iter().map(|(fn_param, _, _, _)| fn_param);
let struct_params = inputs.iter().map(|(_, struct_param, _, _)| struct_param);
let param_names = inputs.iter().map(|(_, _, name, _,)| name);
let type_aliases = inputs.iter().map(|(_, _, _, aliased_param)| aliased_param);
let output = type_gen.resolve_type_path(method.output_ty());
let aliased_module = quote!(
pub mod #method_name {
use super::#types_mod_ident;
#( #type_aliases )*
// Guard the `Output` name against collisions by placing it in a dedicated module.
pub mod output {
let output = type_gen.resolve_type_path(method.output_ty())?;
let aliased_module = quote!(
pub mod #method_name {
use super::#types_mod_ident;
pub type Output = #output;
#( #type_aliases )*
// Guard the `Output` name against collisions by placing it in a dedicated module.
pub mod output {
use super::#types_mod_ident;
pub type Output = #output;
}
}
}
);
);
// From the method metadata generate a structure that holds
// all parameter types. This structure is used with metadata
// to encode parameters to the call via `encode_as_fields_to`.
let derives = type_gen.default_derives();
let struct_name = format_ident!("{}", method.name().to_upper_camel_case());
let struct_input = quote!(
#aliased_module
// From the method metadata generate a structure that holds
// all parameter types. This structure is used with metadata
// to encode parameters to the call via `encode_as_fields_to`.
let derives = type_gen.settings().derives.default_derives();
let struct_name = format_ident!("{}", method.name().to_upper_camel_case());
let struct_input = quote!(
#aliased_module
#derives
pub struct #struct_name {
#( pub #struct_params, )*
}
);
#derives
pub struct #struct_name {
#( pub #struct_params, )*
}
);
let Some(call_hash) = api.method_hash(method.name()) else {
return Err(CodegenError::MissingRuntimeApiMetadata(
trait_name_str.to_owned(),
method_name_str.to_owned(),
))
};
let Some(call_hash) = api.method_hash(method.name()) else {
return Err(CodegenError::MissingRuntimeApiMetadata(
trait_name_str.to_owned(),
method_name_str.to_owned(),
))
};
let method = quote!(
#docs
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, types::#method_name::output::Output> {
#crate_path::runtime_api::Payload::new_static(
#trait_name_str,
#method_name_str,
types::#struct_name { #( #param_names, )* },
[#(#call_hash,)*],
)
}
);
let method = quote!(
#docs
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, types::#method_name::output::Output> {
#crate_path::runtime_api::Payload::new_static(
#trait_name_str,
#method_name_str,
types::#struct_name { #( #param_names, )* },
[#(#call_hash,)*],
)
}
);
Ok((struct_input, method))
}).collect::<Result<_, _>>()?;
Ok((struct_input, method))
})
.collect::<Result<_, _>>()?;
let trait_name = format_ident!("{}", trait_name_str);
let structs = structs_and_methods.iter().map(|(struct_, _)| struct_);
let methods = structs_and_methods.iter().map(|(_, method)| method);
let types_mod_ident = type_gen.types_mod_ident();
let runtime_api = quote!(
pub mod #trait_name_snake {
@@ -175,13 +190,10 @@ pub fn generate_runtime_apis(
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let runtime_fns: Vec<_> = metadata
.runtime_api_traits()
.map(|api| {
generate_runtime_api(api, type_gen, types_mod_ident, crate_path, should_gen_docs)
})
.map(|api| generate_runtime_api(api, type_gen, crate_path))
.collect::<Result<_, _>>()?;
let runtime_apis_def = runtime_fns.iter().map(|(apis, _)| apis);
+28 -28
View File
@@ -2,13 +2,11 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::types::TypeGenerator;
use crate::types::TypePath;
use heck::ToSnakeCase as _;
use heck::ToUpperCamelCase as _;
use heck::{ToSnakeCase as _, ToUpperCamelCase};
use proc_macro2::{Ident, TokenStream as TokenStream2, TokenStream};
use quote::{format_ident, quote};
use scale_info::TypeDef;
use scale_typegen::{typegen::type_path::TypePath, TypeGenerator};
use subxt_metadata::{
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
};
@@ -20,37 +18,26 @@ use super::CodegenError;
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the storages are generated.
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `pallet` - Pallet metadata from which the storages are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the storage items are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
pub fn generate_storage(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let Some(storage) = pallet.storage() else {
return Ok(quote!());
};
let (storage_fns, alias_modules): (Vec<_>, Vec<_>) = storage
let (storage_fns, alias_modules): (Vec<TokenStream2>, Vec<TokenStream2>) = storage
.entries()
.iter()
.map(|entry| {
generate_storage_entry_fns(
type_gen,
pallet,
entry,
crate_path,
should_gen_docs,
types_mod_ident,
)
})
.map(|entry| generate_storage_entry_fns(type_gen, pallet, entry, crate_path))
.collect::<Result<Vec<_>, CodegenError>>()?
.into_iter()
.unzip();
let types_mod_ident = type_gen.types_mod_ident();
Ok(quote! {
pub mod storage {
@@ -71,17 +58,18 @@ pub fn generate_storage(
})
}
/// Returns storage entry functions and alias modules.
fn generate_storage_entry_fns(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
storage_entry: &StorageEntryMetadata,
crate_path: &syn::Path,
should_gen_docs: bool,
types_mod_ident: &syn::Ident,
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
let snake_case_name = storage_entry.name().to_snake_case();
let storage_entry_ty = storage_entry.entry_type().value_ty();
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty);
let storage_entry_value_ty = type_gen
.resolve_type_path(storage_entry_ty)
.expect("storage type is in metadata; qed");
let alias_name = format_ident!("{}", storage_entry.name().to_upper_camel_case());
let alias_module_name = format_ident!("{snake_case_name}");
@@ -89,7 +77,9 @@ fn generate_storage_entry_fns(
let storage_entry_map = |idx, id| {
let ident: Ident = format_ident!("_{}", idx);
let ty_path = type_gen.resolve_type_path(id);
let ty_path = type_gen
.resolve_type_path(id)
.expect("type is in metadata; qed");
let alias_name = format_ident!("Param{}", idx);
let alias_type = primitive_type_alias(&ty_path);
@@ -103,7 +93,11 @@ fn generate_storage_entry_fns(
let keys: Vec<(Ident, TokenStream, TokenStream)> = match storage_entry.entry_type() {
StorageEntryType::Plain(_) => vec![],
StorageEntryType::Map { key_ty, .. } => {
match &type_gen.resolve_type(*key_ty).type_def {
match &type_gen
.resolve_type(*key_ty)
.expect("key type should be present")
.type_def
{
// An N-map; return each of the keys separately.
TypeDef::Tuple(tuple) => tuple
.fields
@@ -128,7 +122,9 @@ fn generate_storage_entry_fns(
};
let docs = storage_entry.docs();
let docs = should_gen_docs
let docs = type_gen
.settings()
.should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
@@ -181,6 +177,7 @@ fn generate_storage_entry_fns(
let alias_types = keys.iter().map(|(_, alias_type, _)| alias_type);
let types_mod_ident = type_gen.types_mod_ident();
// Generate type alias for the return type only, since
// the keys of the storage entry are not explicitly named.
let alias_module = quote! {
@@ -217,9 +214,10 @@ fn primitive_type_alias(type_path: &TypePath) -> TokenStream {
mod tests {
use crate::RuntimeGenerator;
use frame_metadata::v15;
use heck::ToUpperCamelCase as _;
use heck::ToUpperCamelCase;
use quote::{format_ident, quote};
use scale_info::{meta_type, MetaType};
use std::borrow::Cow;
use subxt_metadata::Metadata;
@@ -330,6 +328,8 @@ mod tests {
_0: impl ::std::borrow::Borrow<types::#name_ident::Param0>,
)
);
dbg!(&generated_str);
dbg!(&expected_storage_constructor.to_string());
assert!(generated_str.contains(&expected_storage_constructor.to_string()));
let alias_name = format_ident!("{}", name.to_upper_camel_case());
+5 -50
View File
@@ -5,14 +5,12 @@
//! Errors that can be emitted from codegen.
use proc_macro2::{Span, TokenStream as TokenStream2};
use scale_typegen::TypegenError;
/// Error returned when the Codegen cannot generate the runtime API.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum CodegenError {
/// The given metadata type could not be found.
#[error("Could not find type with ID {0} in the type registry; please raise a support issue.")]
TypeNotFound(u32),
/// Cannot fetch the metadata bytes.
#[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing substrate-based metadata: {0}")]
Fetch(#[from] FetchMetadataError),
@@ -22,12 +20,6 @@ pub enum CodegenError {
/// Out of line modules are not supported.
#[error("Out-of-line subxt modules are not supported, make sure you are providing a body to your module: pub mod polkadot {{ ... }}")]
InvalidModule(Span),
/// Expected named or unnamed fields.
#[error("Fields should either be all named or all unnamed, make sure you are providing a valid metadata: {0}")]
InvalidFields(String),
/// Substitute types must have a valid path.
#[error("Type substitution error: {0}")]
TypeSubstitutionError(#[from] TypeSubstitutionError),
/// Invalid type path.
#[error("Invalid type path {0}: {1}")]
InvalidTypePath(String, syn::Error),
@@ -56,6 +48,9 @@ pub enum CodegenError {
"Extrinsic call type could not be found. Make sure you are providing a valid substrate-based metadata"
)]
MissingCallType,
/// Cannot generate types.
#[error("Type Generation failed: {0}")]
TypeGeneration(#[from] TypegenError),
}
impl CodegenError {
@@ -65,7 +60,7 @@ impl CodegenError {
fn get_location(&self) -> Span {
match self {
Self::InvalidModule(span) => *span,
Self::TypeSubstitutionError(err) => err.get_location(),
Self::TypeGeneration(TypegenError::InvalidSubstitute(err)) => err.span,
Self::InvalidTypePath(_, err) => err.span(),
_ => proc_macro2::Span::call_site(),
}
@@ -102,43 +97,3 @@ pub enum FetchMetadataError {
#[error("Other error: {0}")]
Other(String),
}
/// Error attempting to do type substitution.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TypeSubstitutionError {
/// Substitute "to" type must be an absolute path.
#[error("`substitute_type(with = <path>)` must be a path prefixed with 'crate::' or '::'")]
ExpectedAbsolutePath(Span),
/// Substitute types must have a valid path.
#[error("Substitute types must have a valid path.")]
EmptySubstitutePath(Span),
/// From/To substitution types should use angle bracket generics.
#[error("Expected the from/to type generics to have the form 'Foo<A,B,C..>'.")]
ExpectedAngleBracketGenerics(Span),
/// Source substitute type must be an ident.
#[error("Expected an ident like 'Foo' or 'A' to mark a type to be substituted.")]
InvalidFromType(Span),
/// Target type is invalid.
#[error("Expected an ident like 'Foo' or an absolute concrete path like '::path::to::Bar' for the substitute type.")]
InvalidToType(Span),
/// Target ident doesn't correspond to any source type.
#[error("Cannot find matching param on 'from' type.")]
NoMatchingFromType(Span),
}
impl TypeSubstitutionError {
/// Fetch the location for this error.
// Todo: Probably worth storing location outside of the variant,
// so that there's a common way to set a location for some error.
fn get_location(&self) -> Span {
match self {
TypeSubstitutionError::ExpectedAbsolutePath(span) => *span,
TypeSubstitutionError::EmptySubstitutePath(span) => *span,
TypeSubstitutionError::ExpectedAngleBracketGenerics(span) => *span,
TypeSubstitutionError::InvalidFromType(span) => *span,
TypeSubstitutionError::InvalidToType(span) => *span,
TypeSubstitutionError::NoMatchingFromType(span) => *span,
}
}
}
+177 -34
View File
@@ -9,10 +9,8 @@
#![deny(unused_crate_dependencies, missing_docs)]
mod api;
mod ir;
mod types;
pub mod error;
mod ir;
// These should probably be in a separate crate; they are used by the
// macro and CLI tool, so they only live here because this is a common
@@ -25,14 +23,12 @@ use getrandom as _;
use api::RuntimeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use scale_typegen::{
typegen::settings::substitutes::absolute_path, DerivesRegistry, TypeGeneratorSettings,
TypeSubstitutes, TypegenError,
};
use std::collections::HashMap;
// We expose these only because they are currently needed in subxt-explorer.
// Eventually we'll move the type generation stuff out into a separate crate.
#[doc(hidden)]
pub mod __private {
pub use crate::types::{DerivesRegistry, TypeDefGen, TypeGenerator, TypeSubstitutes};
}
use syn::parse_quote;
// Part of the public interface, so expose:
pub use error::CodegenError;
@@ -72,6 +68,8 @@ pub struct CodegenBuilder {
type_substitutes: HashMap<syn::Path, syn::Path>,
derives_for_type: HashMap<syn::TypePath, Vec<syn::Path>>,
attributes_for_type: HashMap<syn::TypePath, Vec<syn::Attribute>>,
derives_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Path>>,
attributes_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Attribute>>,
}
impl Default for CodegenBuilder {
@@ -90,6 +88,8 @@ impl Default for CodegenBuilder {
type_substitutes: HashMap::new(),
derives_for_type: HashMap::new(),
attributes_for_type: HashMap::new(),
derives_for_type_recursive: HashMap::new(),
attributes_for_type_recursive: HashMap::new(),
}
}
}
@@ -159,33 +159,47 @@ impl CodegenBuilder {
/// Set additional derives for a specific type at the path given.
///
/// # Warning
///
/// For composite types, you may also need to set the same additional derives on all of
/// the contained types as well to avoid compile errors in the generated code.
/// If you want to set the additional derives on all contained types recursively as well,
/// you can set the `recursive` argument to `true`. If you don't do that,
/// there might be compile errors in the generated code, if the derived trait
/// relies on the fact that contained types also implement that trait.
pub fn add_derives_for_type(
&mut self,
ty: syn::TypePath,
derives: impl IntoIterator<Item = syn::Path>,
recursive: bool,
) {
self.derives_for_type.entry(ty).or_default().extend(derives);
if recursive {
self.derives_for_type_recursive
.entry(ty)
.or_default()
.extend(derives);
} else {
self.derives_for_type.entry(ty).or_default().extend(derives);
}
}
/// Set additional attributes for a specific type at the path given.
///
/// # Warning
///
/// For composite types, you may also need to consider contained types and whether they need
/// similar attributes setting.
/// Setting the `recursive` argument to `true` will additionally add the specified
/// attributes to all contained types recursively.
pub fn add_attributes_for_type(
&mut self,
ty: syn::TypePath,
attributes: impl IntoIterator<Item = syn::Attribute>,
recursive: bool,
) {
self.attributes_for_type
.entry(ty)
.or_default()
.extend(attributes);
if recursive {
self.attributes_for_type_recursive
.entry(ty)
.or_default()
.extend(attributes);
} else {
self.attributes_for_type
.entry(ty)
.or_default()
.extend(attributes);
}
}
/// Substitute a type at the given path with some type at the second path. During codegen,
@@ -217,30 +231,39 @@ impl CodegenBuilder {
pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
let crate_path = self.crate_path;
let mut derives_registry = if self.use_default_derives {
types::DerivesRegistry::with_default_derives(&crate_path)
let mut derives_registry: DerivesRegistry = if self.use_default_derives {
default_derives(&crate_path)
} else {
types::DerivesRegistry::new()
DerivesRegistry::new()
};
derives_registry.extend_for_all(self.extra_global_derives, self.extra_global_attributes);
derives_registry.add_derives_for_all(self.extra_global_derives);
derives_registry.add_attributes_for_all(self.extra_global_attributes);
for (ty, derives) in self.derives_for_type {
derives_registry.extend_for_type(ty, derives, vec![]);
derives_registry.add_derives_for(ty, derives, false);
}
for (ty, derives) in self.derives_for_type_recursive {
derives_registry.add_derives_for(ty, derives, true);
}
for (ty, attributes) in self.attributes_for_type {
derives_registry.extend_for_type(ty, vec![], attributes);
derives_registry.add_attributes_for(ty, attributes, false);
}
for (ty, attributes) in self.attributes_for_type_recursive {
derives_registry.add_attributes_for(ty, attributes, true);
}
let mut type_substitutes = if self.use_default_substitutions {
types::TypeSubstitutes::with_default_substitutes(&crate_path)
let mut type_substitutes: TypeSubstitutes = if self.use_default_substitutions {
default_substitutes(&crate_path)
} else {
types::TypeSubstitutes::new()
TypeSubstitutes::new()
};
for (from, with) in self.type_substitutes {
let abs_path = with.try_into()?;
type_substitutes.insert(from, abs_path)?;
let abs_path = absolute_path(with).map_err(TypegenError::from)?;
type_substitutes
.insert(from, abs_path)
.map_err(TypegenError::from)?;
}
let item_mod = self.item_mod;
@@ -266,3 +289,123 @@ impl CodegenBuilder {
}
}
}
/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
let crate_path: syn::Path = parse_quote!(::subxt);
let derives = default_derives(&crate_path);
let substitutes = default_substitutes(&crate_path);
subxt_type_gen_settings(derives, substitutes, &crate_path, true)
}
fn subxt_type_gen_settings(
derives: scale_typegen::DerivesRegistry,
substitutes: scale_typegen::TypeSubstitutes,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> TypeGeneratorSettings {
TypeGeneratorSettings {
types_mod_ident: parse_quote!(runtime_types),
should_gen_docs,
derives,
substitutes,
decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
compact_as_type_path: Some(parse_quote!(#crate_path::ext::codec::CompactAs)),
compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
insert_codec_attributes: true,
}
}
fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
let derives: [syn::Path; 5] = [
parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
parse_quote!(#crate_path::ext::codec::Encode),
parse_quote!(#crate_path::ext::codec::Decode),
parse_quote!(Debug),
];
let attributes: [syn::Attribute; 3] = [
parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
parse_quote!(#[codec(crate = #crate_path::ext::codec)]),
];
let mut derives_registry = DerivesRegistry::new();
derives_registry.add_derives_for_all(derives);
derives_registry.add_attributes_for_all(attributes);
derives_registry
}
fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
let mut type_substitutes = TypeSubstitutes::new();
let defaults: [(syn::Path, syn::Path); 11] = [
(
parse_quote!(bitvec::order::Lsb0),
parse_quote!(#crate_path::utils::bits::Lsb0),
),
(
parse_quote!(bitvec::order::Msb0),
parse_quote!(#crate_path::utils::bits::Msb0),
),
(
parse_quote!(sp_core::crypto::AccountId32),
parse_quote!(#crate_path::utils::AccountId32),
),
(
parse_quote!(sp_runtime::multiaddress::MultiAddress),
parse_quote!(#crate_path::utils::MultiAddress),
),
(
parse_quote!(primitive_types::H160),
parse_quote!(#crate_path::utils::H160),
),
(
parse_quote!(primitive_types::H256),
parse_quote!(#crate_path::utils::H256),
),
(
parse_quote!(primitive_types::H512),
parse_quote!(#crate_path::utils::H512),
),
(
parse_quote!(frame_support::traits::misc::WrapperKeepOpaque),
parse_quote!(#crate_path::utils::WrapperKeepOpaque),
),
// BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
// can cause an issue with generated code that doesn't impl `Ord` by default.
// Decoding them to Vec by default (KeyedVec is just an alias for Vec with
// suitable type params) avoids these issues.
(
parse_quote!(BTreeMap),
parse_quote!(#crate_path::utils::KeyedVec),
),
(parse_quote!(BTreeSet), parse_quote!(::std::vec::Vec)),
// The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
// The inner bytes represent the encoded extrinsic, however when deriving the
// `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
// being altered by adding the length prefix in front of them.
// Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
(
parse_quote!(sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
parse_quote!(#crate_path::utils::UncheckedExtrinsic),
),
];
let defaults = defaults.into_iter().map(|(from, to)| {
(
from,
absolute_path(to).expect("default substitutes above are absolute paths; qed"),
)
});
type_substitutes
.extend(defaults)
.expect("default substitutes can always be parsed; qed");
type_substitutes
}
-395
View File
@@ -1,395 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::CodegenError;
use super::{Derives, Field, TypeDefParameters, TypeGenerator, TypeParameter, TypePath};
use heck::ToUpperCamelCase as _;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use scale_info::{form::PortableForm, Type, TypeDef, TypeDefPrimitive};
/// Representation of a type which consists of a set of fields. Used to generate Rust code for
/// either a standalone `struct` definition, or an `enum` variant.
///
/// Fields can either be named or unnamed in either case.
#[derive(Debug)]
pub struct CompositeDef {
/// The name of the `struct`, or the name of the `enum` variant.
pub name: syn::Ident,
/// Generate either a standalone `struct` or an `enum` variant.
pub kind: CompositeDefKind,
/// The fields of the type, which are either all named or all unnamed.
pub fields: CompositeDefFields,
/// Documentation of the composite type as presented in metadata.
pub docs: Option<TokenStream>,
}
impl CompositeDef {
/// Construct a definition which will generate code for a standalone `struct`.
///
/// This is useful for generating structures from call and enum metadata variants;
/// and from all the runtime types of the metadata.
#[allow(clippy::too_many_arguments)]
pub fn struct_def(
ty: &Type<PortableForm>,
ident: &str,
type_params: TypeDefParameters,
fields_def: CompositeDefFields,
field_visibility: Option<syn::Visibility>,
type_gen: &TypeGenerator,
docs: &[String],
crate_path: &syn::Path,
alias_module_name: Option<syn::Ident>,
) -> Result<Self, CodegenError> {
let mut derives = type_gen.type_derives(ty)?;
let fields: Vec<_> = fields_def.field_types().collect();
if fields.len() == 1 {
// any single field wrapper struct with a concrete unsigned int type can derive
// CompactAs.
let field = &fields[0];
if !type_params
.params()
.iter()
.any(|tp| Some(tp.original_name.to_string()) == field.type_name)
{
let ty = type_gen.resolve_type(field.type_id);
if matches!(
ty.type_def,
TypeDef::Primitive(
TypeDefPrimitive::U8
| TypeDefPrimitive::U16
| TypeDefPrimitive::U32
| TypeDefPrimitive::U64
| TypeDefPrimitive::U128
)
) {
derives.insert_codec_compact_as(crate_path)
}
}
}
let name = format_ident!("{}", ident);
let docs_token = Some(quote! { #( #[doc = #docs ] )* });
Ok(Self {
name,
kind: CompositeDefKind::Struct {
derives,
type_params,
field_visibility,
alias_module_name,
},
fields: fields_def,
docs: docs_token,
})
}
/// Construct a definition which will generate code for an `enum` variant.
pub fn enum_variant_def(ident: &str, fields: CompositeDefFields, docs: &[String]) -> Self {
let name = format_ident!("{}", ident);
let docs_token = Some(quote! { #( #[doc = #docs ] )* });
Self {
name,
kind: CompositeDefKind::EnumVariant,
fields,
docs: docs_token,
}
}
}
impl quote::ToTokens for CompositeDef {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = &self.name;
let docs = &self.docs;
let decl = match &self.kind {
CompositeDefKind::Struct {
derives,
type_params,
field_visibility,
alias_module_name,
} => {
let phantom_data = type_params.unused_params_phantom_data();
let fields: TokenStream = self.fields.to_struct_field_tokens(
phantom_data,
field_visibility.as_ref(),
alias_module_name.as_ref(),
);
let trailing_semicolon = matches!(
self.fields,
CompositeDefFields::NoFields | CompositeDefFields::Unnamed(_)
)
.then(|| quote!(;));
quote! {
#derives
#docs
pub struct #name #type_params #fields #trailing_semicolon
}
}
CompositeDefKind::EnumVariant => {
let fields = self.fields.to_enum_variant_field_tokens();
quote! {
#docs
#name #fields
}
}
};
tokens.extend(decl)
}
}
/// Which kind of composite type are we generating, either a standalone `struct` or an `enum`
/// variant.
#[derive(Debug)]
pub enum CompositeDefKind {
/// Composite type comprising a Rust `struct`.
Struct {
derives: Derives,
type_params: TypeDefParameters,
field_visibility: Option<syn::Visibility>,
alias_module_name: Option<syn::Ident>,
},
/// Comprises a variant of a Rust `enum`.
EnumVariant,
}
/// Encapsulates the composite fields, keeping the invariant that all fields are either named or
/// unnamed.
#[derive(Debug, Clone)]
pub enum CompositeDefFields {
NoFields,
Named(Vec<(syn::Ident, CompositeDefFieldType)>),
Unnamed(Vec<CompositeDefFieldType>),
}
impl CompositeDefFields {
/// Construct a new set of composite fields from the supplied [`::scale_info::Field`]s.
pub fn from_scale_info_fields(
name: &str,
fields: &[Field],
parent_type_params: &[TypeParameter],
type_gen: &TypeGenerator,
) -> Result<Self, CodegenError> {
if fields.is_empty() {
return Ok(Self::NoFields);
}
let mut named_fields = Vec::new();
let mut unnamed_fields = Vec::new();
for field in fields {
let type_path = type_gen.resolve_field_type_path(
field.ty.id,
parent_type_params,
field.type_name.as_deref(),
);
let field_type =
CompositeDefFieldType::new(field.ty.id, type_path, field.type_name.clone());
if let Some(name) = &field.name {
let field_name = format_ident!("{}", name);
named_fields.push((field_name, field_type))
} else {
unnamed_fields.push(field_type)
}
}
if !named_fields.is_empty() && !unnamed_fields.is_empty() {
return Err(CodegenError::InvalidFields(name.into()));
}
let res = if !named_fields.is_empty() {
Self::Named(named_fields)
} else {
Self::Unnamed(unnamed_fields)
};
Ok(res)
}
/// Returns the set of composite fields.
pub fn field_types(&self) -> Box<dyn Iterator<Item = &CompositeDefFieldType> + '_> {
match self {
Self::NoFields => Box::new([].iter()),
Self::Named(named_fields) => Box::new(named_fields.iter().map(|(_, f)| f)),
Self::Unnamed(unnamed_fields) => Box::new(unnamed_fields.iter()),
}
}
/// Generate the code for type aliases which will be used to construct the `struct` or `enum`.
pub fn to_type_aliases_tokens(&self, visibility: Option<&syn::Visibility>) -> TokenStream {
match self {
Self::NoFields => {
quote!()
}
Self::Named(ref fields) => {
let fields = fields.iter().map(|(name, ty)| {
let alias_name = format_ident!("{}", name.to_string().to_upper_camel_case());
// Alias without boxing to have a cleaner call interface.
let ty_path = &ty.type_path;
quote! ( #visibility type #alias_name = #ty_path; )
});
quote!( #( #fields )* )
}
Self::Unnamed(ref fields) => {
let fields = fields.iter().enumerate().map(|(idx, ty)| {
let alias_name = format_ident!("Field{}", idx);
quote! ( #visibility type #alias_name = #ty; )
});
quote!( #( #fields )* )
}
}
}
/// Generate the code for fields which will compose a `struct`.
pub fn to_struct_field_tokens(
&self,
phantom_data: Option<syn::TypePath>,
visibility: Option<&syn::Visibility>,
alias_module_name: Option<&syn::Ident>,
) -> TokenStream {
match self {
Self::NoFields => {
if let Some(phantom_data) = phantom_data {
quote! { ( #phantom_data ) }
} else {
quote! {}
}
}
Self::Named(ref fields) => {
let fields = fields.iter().map(|(name, ty)| {
let compact_attr = ty.compact_attr();
if let Some(alias_module_name) = alias_module_name {
let alias_name =
format_ident!("{}", name.to_string().to_upper_camel_case());
let mut path = quote!( #alias_module_name::#alias_name);
if ty.is_boxed() {
path = quote!( ::std::boxed::Box<#path> );
}
quote! { #compact_attr #visibility #name: #path }
} else {
quote! { #compact_attr #visibility #name: #ty }
}
});
let marker = phantom_data.map(|phantom_data| {
quote!(
#[codec(skip)]
#visibility __subxt_unused_type_params: #phantom_data
)
});
quote!(
{
#( #fields, )*
#marker
}
)
}
Self::Unnamed(ref fields) => {
let fields = fields.iter().enumerate().map(|(idx, ty)| {
let compact_attr = ty.compact_attr();
if let Some(alias_module_name) = alias_module_name {
let alias_name = format_ident!("Field{}", idx);
let mut path = quote!( #alias_module_name::#alias_name);
if ty.is_boxed() {
path = quote!( ::std::boxed::Box<#path> );
}
quote! { #compact_attr #visibility #path }
} else {
quote! { #compact_attr #visibility #ty }
}
});
let marker = phantom_data.map(|phantom_data| {
quote!(
#[codec(skip)]
#visibility #phantom_data
)
});
quote! {
(
#( #fields, )*
#marker
)
}
}
}
}
/// Generate the code for fields which will compose an `enum` variant.
pub fn to_enum_variant_field_tokens(&self) -> TokenStream {
match self {
Self::NoFields => quote! {},
Self::Named(ref fields) => {
let fields = fields.iter().map(|(name, ty)| {
let compact_attr = ty.compact_attr();
quote! { #compact_attr #name: #ty }
});
quote!( { #( #fields, )* } )
}
Self::Unnamed(ref fields) => {
let fields = fields.iter().map(|ty| {
let compact_attr = ty.compact_attr();
quote! { #compact_attr #ty }
});
quote! { ( #( #fields, )* ) }
}
}
}
}
/// Represents a field of a composite type to be generated.
#[derive(Debug, Clone)]
pub struct CompositeDefFieldType {
pub type_id: u32,
pub type_path: TypePath,
pub type_name: Option<String>,
}
impl CompositeDefFieldType {
/// Construct a new [`CompositeDefFieldType`].
pub fn new(type_id: u32, type_path: TypePath, type_name: Option<String>) -> Self {
CompositeDefFieldType {
type_id,
type_path,
type_name,
}
}
/// Returns `true` if the field is a [`::std::boxed::Box`].
pub fn is_boxed(&self) -> bool {
// Use the type name to detect a `Box` field.
// Should be updated once `Box` types are no longer erased:
// https://github.com/paritytech/scale-info/pull/82
matches!(&self.type_name, Some(ty_name) if ty_name.contains("Box<"))
}
/// Returns the `#[codec(compact)]` attribute if the type is compact.
fn compact_attr(&self) -> Option<TokenStream> {
self.type_path
.is_compact()
.then(|| quote!( #[codec(compact)] ))
}
}
impl quote::ToTokens for CompositeDefFieldType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ty_path = &self.type_path;
if self.is_boxed() {
tokens.extend(quote! { ::std::boxed::Box<#ty_path> })
} else {
tokens.extend(quote! { #ty_path })
};
}
}
-199
View File
@@ -1,199 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use syn::{parse_quote, Path};
use std::collections::{HashMap, HashSet};
/// A struct containing the derives that we'll be applying to types;
/// a combination of some common derives for all types, plus type
/// specific derives.
#[derive(Debug, Clone)]
pub struct DerivesRegistry {
default_derives: Derives,
specific_type_derives: HashMap<syn::TypePath, Derives>,
}
impl Default for DerivesRegistry {
fn default() -> Self {
Self::new()
}
}
impl DerivesRegistry {
/// Creates a new `DerivesRegistry` with no default derives.
pub fn new() -> Self {
Self {
default_derives: Derives::new(),
specific_type_derives: Default::default(),
}
}
/// Creates a new `DerivesRegistry` with default derives.
///
/// The `crate_path` denotes the `subxt` crate access path in the
/// generated code.
pub fn with_default_derives(crate_path: &syn::Path) -> Self {
Self {
default_derives: Derives::with_defaults(crate_path),
specific_type_derives: Default::default(),
}
}
/// Insert derives to be applied to all generated types.
pub fn extend_for_all(
&mut self,
derives: impl IntoIterator<Item = syn::Path>,
attributes: impl IntoIterator<Item = syn::Attribute>,
) {
self.default_derives.derives.extend(derives);
self.default_derives.attributes.extend(attributes);
}
/// Insert derives to be applied to a specific generated type.
pub fn extend_for_type(
&mut self,
ty: syn::TypePath,
derives: impl IntoIterator<Item = syn::Path>,
attributes: impl IntoIterator<Item = syn::Attribute>,
) {
let type_derives = self.specific_type_derives.entry(ty).or_default();
type_derives.derives.extend(derives);
type_derives.attributes.extend(attributes);
}
/// Returns the derives to be applied to all generated types.
pub fn default_derives(&self) -> &Derives {
&self.default_derives
}
/// Resolve the derives for a generated type. Includes:
/// - The default derives for all types e.g. `scale::Encode, scale::Decode`
/// - Any user-defined derives for all types via `generated_type_derives`
/// - Any user-defined derives for this specific type
pub fn resolve(&self, ty: &syn::TypePath) -> Derives {
let mut resolved_derives = self.default_derives.clone();
if let Some(specific) = self.specific_type_derives.get(ty) {
resolved_derives.extend_from(specific.clone());
}
resolved_derives
}
}
/// A struct storing the set of derives and derive attributes that we'll apply
/// to generated types.
#[derive(Debug, Clone)]
pub struct Derives {
derives: HashSet<syn::Path>,
attributes: HashSet<syn::Attribute>,
}
impl Default for Derives {
fn default() -> Self {
Self::new()
}
}
impl FromIterator<syn::Path> for Derives {
fn from_iter<T: IntoIterator<Item = Path>>(iter: T) -> Self {
let derives = iter.into_iter().collect();
Self {
derives,
attributes: HashSet::new(),
}
}
}
impl Derives {
/// Creates an empty instance of `Derives` (with no default derives).
pub fn new() -> Self {
Self {
derives: HashSet::new(),
attributes: HashSet::new(),
}
}
/// Creates a new instance of `Derives` with the `crate_path` prepended
/// to the set of default derives that reside in `subxt`.
pub fn with_defaults(crate_path: &syn::Path) -> Self {
let mut derives = HashSet::new();
let mut attributes = HashSet::new();
derives.insert(syn::parse_quote!(#crate_path::ext::scale_encode::EncodeAsType));
let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
attributes.insert(syn::parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]));
derives.insert(syn::parse_quote!(#crate_path::ext::scale_decode::DecodeAsType));
let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
attributes.insert(syn::parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]));
derives.insert(syn::parse_quote!(#crate_path::ext::codec::Encode));
derives.insert(syn::parse_quote!(#crate_path::ext::codec::Decode));
attributes.insert(syn::parse_quote!(#[codec(crate = #crate_path::ext::codec)]));
derives.insert(syn::parse_quote!(Debug));
Self {
derives,
attributes,
}
}
/// Extend this set of `Derives` from another.
pub fn extend_from(&mut self, other: Derives) {
self.derives.extend(other.derives);
self.attributes.extend(other.attributes);
}
/// Add `#crate_path::ext::codec::CompactAs` to the derives.
pub fn insert_codec_compact_as(&mut self, crate_path: &syn::Path) {
self.insert_derive(parse_quote!(#crate_path::ext::codec::CompactAs));
}
/// Extend the set of derives by providing an iterator of paths to derive macros.
pub fn extend(&mut self, derives: impl Iterator<Item = syn::Path>) {
for derive in derives {
self.insert_derive(derive)
}
}
/// Insert a single derive.
pub fn insert_derive(&mut self, derive: syn::Path) {
self.derives.insert(derive);
}
/// Insert a single attribute to be applied to types.
pub fn insert_attribute(&mut self, attribute: syn::Attribute) {
self.attributes.insert(attribute);
}
}
impl quote::ToTokens for Derives {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
if !self.derives.is_empty() {
let mut sorted = self.derives.iter().cloned().collect::<Vec<_>>();
sorted.sort_by(|a, b| {
quote::quote!(#a)
.to_string()
.cmp(&quote::quote!(#b).to_string())
});
tokens.extend(quote::quote! {
#[derive(#( #sorted ),*)]
})
}
if !self.attributes.is_empty() {
let mut sorted = self.attributes.iter().cloned().collect::<Vec<_>>();
sorted.sort_by(|a, b| {
quote::quote!(#a)
.to_string()
.cmp(&quote::quote!(#b).to_string())
});
tokens.extend(quote::quote! {
#( #sorted )*
})
}
}
}
-350
View File
@@ -1,350 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
mod composite_def;
mod derives;
mod substitutes;
#[cfg(test)]
mod tests;
mod type_def;
mod type_def_params;
mod type_path;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef};
use std::collections::BTreeMap;
use crate::error::CodegenError;
pub use self::{
composite_def::{CompositeDef, CompositeDefFieldType, CompositeDefFields},
derives::{Derives, DerivesRegistry},
substitutes::TypeSubstitutes,
type_def::TypeDefGen,
type_def_params::TypeDefParameters,
type_path::{TypeParameter, TypePath, TypePathType},
};
pub type Field = scale_info::Field<PortableForm>;
/// Generate a Rust module containing all types defined in the supplied [`PortableRegistry`].
#[derive(Debug)]
pub struct TypeGenerator<'a> {
/// The name of the module which will contain the generated types.
types_mod_ident: Ident,
/// Registry of type definitions to be transformed into Rust type definitions.
type_registry: &'a PortableRegistry,
/// User defined overrides for generated types.
type_substitutes: TypeSubstitutes,
/// Set of derives with which to annotate generated types.
derives: DerivesRegistry,
/// The `subxt` crate access path in the generated code.
crate_path: syn::Path,
/// True if codegen should generate the documentation for the API.
should_gen_docs: bool,
}
impl<'a> TypeGenerator<'a> {
/// Construct a new [`TypeGenerator`].
pub fn new(
type_registry: &'a PortableRegistry,
root_mod: &'static str,
type_substitutes: TypeSubstitutes,
derives: DerivesRegistry,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Self {
let root_mod_ident = Ident::new(root_mod, Span::call_site());
Self {
types_mod_ident: root_mod_ident,
type_registry,
type_substitutes,
derives,
crate_path,
should_gen_docs,
}
}
/// Generate a module containing all types defined in the supplied type registry.
pub fn generate_types_mod(&self) -> Result<Module, CodegenError> {
let root_mod_ident = &self.types_mod_ident;
let mut root_mod = Module::new(root_mod_ident.clone(), root_mod_ident.clone());
for ty in &self.type_registry.types {
let path = &ty.ty.path;
// Don't generate a type if it was substituted - the target type might
// not be in the type registry + our resolution already performs the substitution.
if self.type_substitutes.contains(path) {
continue;
}
let namespace = path.namespace();
// prelude types e.g. Option/Result have no namespace, so we don't generate them
if namespace.is_empty() {
continue;
}
// Lazily create submodules for the encountered namespace path, if they don't exist
let innermost_module = namespace
.iter()
.map(|segment| Ident::new(segment, Span::call_site()))
.fold(&mut root_mod, |module, ident| {
module
.children
.entry(ident.clone())
.or_insert_with(|| Module::new(ident, root_mod_ident.clone()))
});
innermost_module
.types
.insert(path.clone(), TypeDefGen::from_type(&ty.ty, self)?);
}
Ok(root_mod)
}
/// # Panics
///
/// If no type with the given id found in the type registry.
pub fn resolve_type(&self, id: u32) -> Type<PortableForm> {
self.type_registry
.resolve(id)
.unwrap_or_else(|| panic!("No type with id {id} found"))
.clone()
}
/// Get the type path for a field of a struct or an enum variant, providing any generic
/// type parameters from the containing type. This is for identifying where a generic type
/// parameter is used in a field type e.g.
///
/// ```rust
/// struct S<T> {
/// a: T, // `T` is the "parent" type param from the containing type.
/// b: Vec<Option<T>>, // nested use of generic type param `T`.
/// }
/// ```
///
/// This allows generating the correct generic field type paths.
///
/// # Panics
///
/// If no type with the given id found in the type registry.
pub fn resolve_field_type_path(
&self,
id: u32,
parent_type_params: &[TypeParameter],
original_name: Option<&str>,
) -> TypePath {
self.resolve_type_path_recurse(id, true, parent_type_params, original_name)
}
/// Get the type path for the given type identifier.
///
/// # Panics
///
/// If no type with the given id found in the type registry.
pub fn resolve_type_path(&self, id: u32) -> TypePath {
self.resolve_type_path_recurse(id, false, &[], None)
}
/// Visit each node in a possibly nested type definition to produce a type path.
///
/// e.g `Result<GenericStruct<NestedGenericStruct<T>>, String>`
///
/// if `original_name` is `Some(original_name)`, the resolved type needs to have the same `original_name`.
fn resolve_type_path_recurse(
&self,
id: u32,
is_field: bool,
parent_type_params: &[TypeParameter],
original_name: Option<&str>,
) -> TypePath {
if let Some(parent_type_param) = parent_type_params.iter().find(|tp| {
tp.concrete_type_id == id
&& original_name.map_or(true, |original_name| tp.original_name == original_name)
}) {
return TypePath::from_parameter(parent_type_param.clone());
}
let mut ty = self.resolve_type(id);
if ty.path.ident() == Some("Cow".to_string()) {
ty = self.resolve_type(
ty.type_params[0]
.ty
.expect("type parameters to Cow are not expected to be skipped; qed")
.id,
)
}
let params: Vec<_> = ty
.type_params
.iter()
.filter_map(|f| {
f.ty.map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
})
.collect();
let ty = match &ty.type_def {
TypeDef::Composite(_) | TypeDef::Variant(_) => {
if let Some(ty) = self
.type_substitutes
.for_path_with_params(&ty.path, &params)
{
ty
} else {
TypePathType::from_type_def_path(&ty.path, self.types_mod_ident.clone(), params)
}
}
TypeDef::Primitive(primitive) => TypePathType::Primitive {
def: primitive.clone(),
},
TypeDef::Array(arr) => TypePathType::Array {
len: arr.len as usize,
of: Box::new(self.resolve_type_path_recurse(
arr.type_param.id,
false,
parent_type_params,
None,
)),
},
TypeDef::Sequence(seq) => TypePathType::Vec {
of: Box::new(self.resolve_type_path_recurse(
seq.type_param.id,
false,
parent_type_params,
None,
)),
},
TypeDef::Tuple(tuple) => TypePathType::Tuple {
elements: tuple
.fields
.iter()
.map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
.collect(),
},
TypeDef::Compact(compact) => TypePathType::Compact {
inner: Box::new(self.resolve_type_path_recurse(
compact.type_param.id,
false,
parent_type_params,
None,
)),
is_field,
crate_path: self.crate_path.clone(),
},
TypeDef::BitSequence(bitseq) => TypePathType::BitVec {
bit_order_type: Box::new(self.resolve_type_path_recurse(
bitseq.bit_order_type.id,
false,
parent_type_params,
None,
)),
bit_store_type: Box::new(self.resolve_type_path_recurse(
bitseq.bit_store_type.id,
false,
parent_type_params,
None,
)),
crate_path: self.crate_path.clone(),
},
};
TypePath::from_type(ty)
}
/// Returns the derives to be applied to all generated types.
pub fn default_derives(&self) -> &Derives {
self.derives.default_derives()
}
/// Returns the type registry.
pub fn types(&self) -> &PortableRegistry {
self.type_registry
}
/// Returns the derives to be applied to a generated type.
pub fn type_derives(&self, ty: &Type<PortableForm>) -> Result<Derives, CodegenError> {
let joined_path = ty.path.segments.join("::");
let ty_path: syn::TypePath = syn::parse_str(&joined_path)
.map_err(|e| CodegenError::InvalidTypePath(joined_path, e))?;
Ok(self.derives.resolve(&ty_path))
}
/// The name of the module which will contain the generated types.
pub fn types_mod_ident(&self) -> &Ident {
&self.types_mod_ident
}
/// The `subxt` crate access path in the generated code.
pub fn crate_path(&self) -> &syn::Path {
&self.crate_path
}
/// True if codegen should generate the documentation for the API.
pub fn should_gen_docs(&self) -> bool {
self.should_gen_docs
}
}
/// Represents a Rust `mod`, containing generated types and child `mod`s.
#[derive(Debug)]
pub struct Module {
name: Ident,
root_mod: Ident,
children: BTreeMap<Ident, Module>,
types: BTreeMap<scale_info::Path<PortableForm>, TypeDefGen>,
}
impl ToTokens for Module {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = &self.name;
let root_mod = &self.root_mod;
let modules = self.children.values();
let types = self.types.values().clone();
tokens.extend(quote! {
pub mod #name {
use super::#root_mod;
#( #modules )*
#( #types )*
}
})
}
}
impl Module {
/// Create a new [`Module`], with a reference to the root `mod` for resolving type paths.
pub(crate) fn new(name: Ident, root_mod: Ident) -> Self {
Self {
name,
root_mod,
children: BTreeMap::new(),
types: BTreeMap::new(),
}
}
/// Returns the module ident.
pub fn ident(&self) -> &Ident {
&self.name
}
/// Returns this `Module`s child `mod`s.
pub fn children(&self) -> impl Iterator<Item = (&Ident, &Module)> {
self.children.iter()
}
/// Returns the generated types.
pub fn types(&self) -> impl Iterator<Item = (&scale_info::Path<PortableForm>, &TypeDefGen)> {
self.types.iter()
}
/// Returns the root `mod` used for resolving type paths.
pub fn root_mod(&self) -> &Ident {
&self.root_mod
}
}
-529
View File
@@ -1,529 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::TypeSubstitutionError;
use std::{borrow::Borrow, collections::HashMap};
use syn::{parse_quote, spanned::Spanned as _};
use super::{TypePath, TypePathType};
/// A map of type substitutes. We match on the paths to generated types in order
/// to figure out when to swap said type with some provided substitute.
#[derive(Debug)]
pub struct TypeSubstitutes {
substitutes: HashMap<PathSegments, Substitute>,
}
#[derive(Debug)]
struct Substitute {
path: syn::Path,
param_mapping: TypeParamMapping,
}
#[derive(Debug)]
enum TypeParamMapping {
// Pass any generics from source to target type
PassThrough,
// Replace any ident seen in the path with the input generic type at this index
Specified(Vec<(syn::Ident, usize)>),
}
macro_rules! path_segments {
($($ident: ident)::*) => {
PathSegments(
[$(stringify!($ident)),*].into_iter().map(String::from).collect::<Vec<_>>()
)
}
}
impl Default for TypeSubstitutes {
fn default() -> Self {
Self::new()
}
}
impl TypeSubstitutes {
/// Creates a new `TypeSubstitutes` with no default derives.
pub fn new() -> Self {
Self {
substitutes: HashMap::new(),
}
}
/// Creates a new `TypeSubstitutes` with some default substitutions in place.
///
/// The `crate_path` denotes the `subxt` crate access path in the
/// generated code.
pub fn with_default_substitutes(crate_path: &syn::Path) -> Self {
// Some hardcoded default type substitutes, can be overridden by user
let defaults = [
(
path_segments!(bitvec::order::Lsb0),
parse_quote!(#crate_path::utils::bits::Lsb0),
),
(
path_segments!(bitvec::order::Msb0),
parse_quote!(#crate_path::utils::bits::Msb0),
),
(
path_segments!(sp_core::crypto::AccountId32),
parse_quote!(#crate_path::utils::AccountId32),
),
(
path_segments!(sp_runtime::multiaddress::MultiAddress),
parse_quote!(#crate_path::utils::MultiAddress),
),
(
path_segments!(primitive_types::H160),
parse_quote!(#crate_path::utils::H160),
),
(
path_segments!(primitive_types::H256),
parse_quote!(#crate_path::utils::H256),
),
(
path_segments!(primitive_types::H512),
parse_quote!(#crate_path::utils::H512),
),
(
path_segments!(frame_support::traits::misc::WrapperKeepOpaque),
parse_quote!(#crate_path::utils::WrapperKeepOpaque),
),
// BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
// can cause an issue with generated code that doesn't impl `Ord` by default.
// Decoding them to Vec by default (KeyedVec is just an alias for Vec with
// suitable type params) avoids these issues.
(
path_segments!(BTreeMap),
parse_quote!(#crate_path::utils::KeyedVec),
),
(path_segments!(BTreeSet), parse_quote!(::std::vec::Vec)),
// The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
// The inner bytes represent the encoded extrinsic, however when deriving the
// `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
// being altered by adding the length prefix in front of them.
(
path_segments!(sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
parse_quote!(#crate_path::utils::UncheckedExtrinsic),
),
];
let default_substitutes = defaults
.into_iter()
.map(|(k, v)| {
(
k,
Substitute {
path: v,
param_mapping: TypeParamMapping::PassThrough,
},
)
})
.collect();
Self {
substitutes: default_substitutes,
}
}
/// Insert the given substitution, overwriting any other with the same path.
pub fn insert(
&mut self,
source: syn::Path,
target: AbsolutePath,
) -> Result<(), TypeSubstitutionError> {
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
self.substitutes.insert(key, val);
Ok(())
}
/// Only insert the given substitution if a substitution at that path doesn't
/// already exist.
pub fn insert_if_not_exists(
&mut self,
source: syn::Path,
target: AbsolutePath,
) -> Result<(), TypeSubstitutionError> {
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
self.substitutes.entry(key).or_insert(val);
Ok(())
}
/// Add a bunch of source to target type substitutions.
pub fn extend(
&mut self,
elems: impl IntoIterator<Item = (syn::Path, AbsolutePath)>,
) -> Result<(), TypeSubstitutionError> {
for (source, target) in elems.into_iter() {
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
self.substitutes.insert(key, val);
}
Ok(())
}
/// Given a source and target path, parse the type params to work out the mapping from
/// source to target, and output the source => substitution mapping that we work out from this.
fn parse_path_substitution(
src_path: syn::Path,
target_path: syn::Path,
) -> Result<(PathSegments, Substitute), TypeSubstitutionError> {
let param_mapping = Self::parse_path_param_mapping(&src_path, &target_path)?;
Ok((
PathSegments::from(&src_path),
Substitute {
// Note; at this point, target_path might have some generics still. These
// might be hardcoded types that we want to keep, so leave them here for now.
path: target_path,
param_mapping,
},
))
}
/// Given a source and target path, parse the type params to work out the mapping from
/// source to target, and return it.
fn parse_path_param_mapping(
src_path: &syn::Path,
target_path: &syn::Path,
) -> Result<TypeParamMapping, TypeSubstitutionError> {
let Some(syn::PathSegment {
arguments: src_path_args,
..
}) = src_path.segments.last()
else {
return Err(TypeSubstitutionError::EmptySubstitutePath(src_path.span()));
};
let Some(syn::PathSegment {
arguments: target_path_args,
..
}) = target_path.segments.last()
else {
return Err(TypeSubstitutionError::EmptySubstitutePath(
target_path.span(),
));
};
// Get hold of the generic args for the "from" type, erroring if they aren't valid.
let source_args = match src_path_args {
syn::PathArguments::None => {
// No generics defined on the source type:
Vec::new()
}
syn::PathArguments::AngleBracketed(args) => {
// We have generics like <A,B> defined on the source type (error for any non-ident type):
args.args
.iter()
.map(|arg| match get_valid_from_substitution_type(arg) {
Some(ident) => Ok(ident),
None => Err(TypeSubstitutionError::InvalidFromType(arg.span())),
})
.collect::<Result<Vec<_>, _>>()?
}
syn::PathArguments::Parenthesized(args) => {
// Generics like (A,B) -> defined; not allowed:
return Err(TypeSubstitutionError::ExpectedAngleBracketGenerics(
args.span(),
));
}
};
// Get hold of the generic args for the "to" type, erroring if they aren't valid.
let target_args = match target_path_args {
syn::PathArguments::None => {
// No generics on target.
Vec::new()
}
syn::PathArguments::AngleBracketed(args) => {
// We have generics like <A,B> defined on the target type.
args.args
.iter()
.map(|arg| match get_valid_to_substitution_type(arg) {
Some(arg) => Ok(arg),
None => Err(TypeSubstitutionError::InvalidToType(arg.span())),
})
.collect::<Result<Vec<_>, _>>()?
}
syn::PathArguments::Parenthesized(args) => {
// Generics like (A,B) -> defined; not allowed:
return Err(TypeSubstitutionError::ExpectedAngleBracketGenerics(
args.span(),
));
}
};
// If no generics defined on source or target, we just apply any concrete generics
// to the substitute type.
if source_args.is_empty() && target_args.is_empty() {
return Ok(TypeParamMapping::PassThrough);
}
// Make a note of the idents in the source args and their indexes.
let mapping = source_args
.into_iter()
.enumerate()
.map(|(idx, ident)| (ident.clone(), idx))
.collect();
Ok(TypeParamMapping::Specified(mapping))
}
/// Given a source type path, return whether a substitute exists for it.
pub fn contains(&self, path: impl Into<PathSegments>) -> bool {
self.substitutes.contains_key(&path.into())
}
/// Given a source type path and the resolved, supplied type parameters,
/// return a new path and optionally overwritten type parameters.
pub fn for_path_with_params(
&self,
path: impl Into<PathSegments>,
params: &[TypePath],
) -> Option<TypePathType> {
// If we find a substitute type, we'll take the substitute path, and
// swap any idents with their new concrete types.
fn replace_params(
mut substitute_path: syn::Path,
params: &[TypePath],
mapping: &TypeParamMapping,
) -> TypePathType {
match mapping {
// We need to map the input params to the output params somehow:
TypeParamMapping::Specified(mapping) => {
// A map from ident name to replacement path.
let replacement_map: Vec<(&syn::Ident, &TypePath)> = mapping
.iter()
.filter_map(|(ident, idx)| params.get(*idx).map(|param| (ident, param)))
.collect();
// Replace params in our substitute path with the incoming ones as needed.
// No need if no replacements given.
if !replacement_map.is_empty() {
replace_path_params_recursively(&mut substitute_path, &replacement_map);
}
TypePathType::Path {
path: substitute_path,
params: Vec::new(),
}
}
// No mapping; just hand back the substitute path and input params.
TypeParamMapping::PassThrough => TypePathType::Path {
path: substitute_path,
params: params.to_vec(),
},
}
}
let path = path.into();
self.substitutes
.get(&path)
.map(|sub| replace_params(sub.path.clone(), params, &sub.param_mapping))
}
}
/// Identifiers joined by the `::` separator.
///
/// We use this as a common denominator, since we need a consistent keys for both
/// `syn::TypePath` and `scale_info::ty::path::Path` types.
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct PathSegments(Vec<String>);
impl From<&syn::Path> for PathSegments {
fn from(path: &syn::Path) -> Self {
PathSegments(path.segments.iter().map(|x| x.ident.to_string()).collect())
}
}
impl<T: scale_info::form::Form> From<&scale_info::Path<T>> for PathSegments {
fn from(path: &scale_info::Path<T>) -> Self {
PathSegments(
path.segments
.iter()
.map(|x| x.as_ref().to_owned())
.collect(),
)
}
}
/// Dig through a `syn::TypePath` (this is provided by the user in a type substitution definition as the "to" type) and
/// swap out any type params which match the idents given in the "from" type with the corresponding concrete types.
///
/// eg if we have:
///
/// ```text
/// from = sp_runtime::MultiAddress<A, B>,
/// to = ::subxt::utils::Static<::sp_runtime::MultiAddress<A, B>>
/// ```
///
/// And we encounter a `sp_runtime::MultiAddress<Foo, Bar>`, then we will pass the `::sp_runtime::MultiAddress<A, B>`
/// type param value into this call to turn it into `::sp_runtime::MultiAddress<Foo, Bar>`.
fn replace_path_params_recursively<I: Borrow<syn::Ident>, P: Borrow<TypePath>>(
path: &mut syn::Path,
params: &Vec<(I, P)>,
) {
for segment in &mut path.segments {
let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments else {
continue;
};
for arg in &mut args.args {
let syn::GenericArgument::Type(ty) = arg else {
continue;
};
let syn::Type::Path(path) = ty else {
continue;
};
if let Some(ident) = get_ident_from_type_path(path) {
if let Some((_, replacement)) = params.iter().find(|(i, _)| ident == i.borrow()) {
*ty = replacement.borrow().to_syn_type();
continue;
}
}
replace_path_params_recursively(&mut path.path, params);
}
}
}
/// Given a "to" type in a type substitution, return the TypePath inside or None if
/// it's not a valid "to" type.
fn get_valid_to_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::TypePath> {
let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
// We are looking for a type, not a lifetime or anything else
return None;
};
Some(type_path)
}
/// Given a "from" type in a type substitution, return the Ident inside or None if
/// it's not a valid "from" type.
fn get_valid_from_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::Ident> {
let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
// We are looking for a type, not a lifetime or anything else
return None;
};
get_ident_from_type_path(type_path)
}
/// Given a type path, return the single ident representing it if that's all it is.
fn get_ident_from_type_path(type_path: &syn::TypePath) -> Option<&syn::Ident> {
if type_path.qself.is_some() {
// No "<Foo as Bar>" type thing
return None;
}
if type_path.path.leading_colon.is_some() {
// No leading "::"
return None;
}
if type_path.path.segments.len() > 1 {
// The path should just be a single ident, not multiple
return None;
}
let Some(segment) = type_path.path.segments.last() else {
// Get the single ident (should be infallible)
return None;
};
if !segment.arguments.is_empty() {
// The ident shouldn't have any of it's own generic args like A<B, C>
return None;
}
Some(&segment.ident)
}
/// Whether a path is absolute - starts with `::` or `crate`.
fn is_absolute(path: &syn::Path) -> bool {
path.leading_colon.is_some()
|| path
.segments
.first()
.map_or(false, |segment| segment.ident == "crate")
}
pub struct AbsolutePath(pub syn::Path);
impl TryFrom<syn::Path> for AbsolutePath {
type Error = TypeSubstitutionError;
fn try_from(value: syn::Path) -> Result<Self, Self::Error> {
if is_absolute(&value) {
Ok(AbsolutePath(value))
} else {
Err(TypeSubstitutionError::ExpectedAbsolutePath(value.span()))
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! syn_path {
($path:path) => {{
let path: syn::Path = syn::parse_quote!($path);
path
}};
}
macro_rules! type_path {
($path:path) => {{
let path: syn::Path = syn::parse_quote!($path);
TypePath::from_syn_path(path)
}};
}
fn ident(name: &'static str) -> syn::Ident {
syn::Ident::new(name, proc_macro2::Span::call_site())
}
#[test]
#[rustfmt::skip]
fn replacing_nested_type_params_works() {
// Original path, replacement ident->paths, expected output path
let paths = [
// Works ok if nothing to replace
(
syn_path!(::some::path::Foo<::other::Path<A, B>>),
vec![],
syn_path!(::some::path::Foo<::other::Path<A, B>>),
),
// Simple top level replacing
(
syn_path!(::some::path::Foo<A>),
vec![(ident("A"), type_path!(::new::Value))],
syn_path!(::some::path::Foo<::new::Value>),
),
// More deeply nested replacing works too
(
syn_path!(::some::path::Foo<::other::Path<A, B>>),
vec![(ident("A"), type_path!(::new::Value))],
syn_path!(::some::path::Foo<::other::Path<::new::Value, B>>),
),
(
syn_path!(::some::path::Foo<::other::Path<A, B>>),
vec![
(ident("A"), type_path!(::new::A)),
(ident("B"), type_path!(::new::B)),
],
syn_path!(::some::path::Foo<::other::Path<::new::A, ::new::B>>),
),
(
syn_path!(::some::path::Foo<::other::Path<A, ::more::path::to<::something::Argh<B>>>, C>),
vec![
(ident("A"), type_path!(::new::A)),
(ident("B"), type_path!(::new::B)),
],
syn_path!(::some::path::Foo<::other::Path<::new::A, ::more::path::to<::something::Argh<::new::B>>>,C>),
),
// The same ident will be replaced as many times as needed:
(
syn_path!(::some::path::Foo<::other::Path<A, ::foo::Argh<A, B>, A>>),
vec![(ident("A"), type_path!(::new::Value))],
syn_path!(::some::path::Foo<::other::Path<::new::Value, ::foo::Argh<::new::Value, B>, ::new::Value>>),
),
];
for (mut path, replacements, expected) in paths {
replace_path_params_recursively(&mut path, &replacements);
assert_eq!(path, expected);
}
}
}
File diff suppressed because it is too large Load Diff
-174
View File
@@ -1,174 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::CodegenError;
use super::{
CompositeDef, CompositeDefFields, Derives, TypeDefParameters, TypeGenerator, TypeParameter,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use scale_info::{form::PortableForm, Type, TypeDef};
use syn::parse_quote;
/// Generates a Rust `struct` or `enum` definition based on the supplied [`scale-info::Type`].
///
/// Field type paths are resolved via the `TypeGenerator`, which contains the registry of all
/// generated types in the module.
#[derive(Debug)]
pub struct TypeDefGen {
/// The type parameters of the type to be generated
type_params: TypeDefParameters,
/// The derives with which to annotate the generated type.
derives: Derives,
/// The kind of type to be generated.
ty_kind: TypeDefGenKind,
/// Type documentation.
ty_docs: TokenStream,
}
impl TypeDefGen {
/// Construct a type definition for codegen from the given [`scale_info::Type`].
pub fn from_type(
ty: &Type<PortableForm>,
type_gen: &TypeGenerator,
) -> Result<Self, CodegenError> {
let derives = type_gen.type_derives(ty)?;
let crate_path = type_gen.crate_path();
let should_gen_docs = type_gen.should_gen_docs();
let type_params = ty
.type_params
.iter()
.enumerate()
.filter_map(|(i, tp)| match &tp.ty {
Some(ty) => {
let tp_name = format_ident!("_{}", i);
Some(TypeParameter {
concrete_type_id: ty.id,
original_name: tp.name.clone(),
name: tp_name,
})
}
None => None,
})
.collect::<Vec<_>>();
let mut type_params = TypeDefParameters::new(type_params);
let ty_kind = match &ty.type_def {
TypeDef::Composite(composite) => {
let type_name = ty.path.ident().expect("structs should have a name");
let fields = CompositeDefFields::from_scale_info_fields(
&type_name,
&composite.fields,
type_params.params(),
type_gen,
)?;
type_params.update_unused(fields.field_types());
let docs = should_gen_docs.then_some(&*ty.docs).unwrap_or_default();
let composite_def = CompositeDef::struct_def(
ty,
&type_name,
type_params.clone(),
fields,
Some(parse_quote!(pub)),
type_gen,
docs,
crate_path,
None,
)?;
TypeDefGenKind::Struct(composite_def)
}
TypeDef::Variant(variant) => {
let type_name = ty.path.ident().expect("variants should have a name");
let variants = variant
.variants
.iter()
.map(|v| {
let fields = CompositeDefFields::from_scale_info_fields(
&v.name,
&v.fields,
type_params.params(),
type_gen,
)?;
type_params.update_unused(fields.field_types());
let docs = should_gen_docs.then_some(&*v.docs).unwrap_or_default();
let variant_def = CompositeDef::enum_variant_def(&v.name, fields, docs);
Ok((v.index, variant_def))
})
.collect::<Result<Vec<_>, CodegenError>>()?;
TypeDefGenKind::Enum(type_name, variants)
}
_ => TypeDefGenKind::BuiltIn,
};
let docs = &ty.docs;
let ty_docs = should_gen_docs
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
Ok(Self {
type_params,
derives,
ty_kind,
ty_docs,
})
}
/// are there unused type params?
pub fn has_unused_type_params(&self) -> bool {
self.type_params.has_unused_type_params()
}
}
impl quote::ToTokens for TypeDefGen {
fn to_tokens(&self, tokens: &mut TokenStream) {
match &self.ty_kind {
TypeDefGenKind::Struct(composite) => composite.to_tokens(tokens),
TypeDefGenKind::Enum(type_name, variants) => {
let mut variants = variants
.iter()
.map(|(index, def)| {
let index = proc_macro2::Literal::u8_unsuffixed(*index);
quote! {
#[codec(index = #index)]
#def
}
})
.collect::<Vec<_>>();
if let Some(phantom) = self.type_params.unused_params_phantom_data() {
variants.push(quote! {
__Ignore(#phantom)
})
}
let enum_ident = format_ident!("{}", type_name);
let type_params = &self.type_params;
let derives = &self.derives;
let docs = &self.ty_docs;
let ty_toks = quote! {
#derives
#docs
pub enum #enum_ident #type_params {
#( #variants, )*
}
};
tokens.extend(ty_toks);
}
TypeDefGenKind::BuiltIn => (), /* all built-in types should already be in scope */
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum TypeDefGenKind {
Struct(CompositeDef),
Enum(String, Vec<(u8, CompositeDef)>),
BuiltIn,
}
-77
View File
@@ -1,77 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use super::TypeParameter;
use crate::types::CompositeDefFieldType;
use quote::quote;
use std::collections::BTreeSet;
/// Represents the set of generic type parameters for generating a type definition e.g. the `T` in
/// `Foo<T>`.
///
/// Additionally this allows generating a `PhantomData` type for any type params which are unused
/// in the type definition itself.
#[derive(Clone, Debug, Default)]
pub struct TypeDefParameters {
params: Vec<TypeParameter>,
unused: BTreeSet<TypeParameter>,
}
impl TypeDefParameters {
/// Create a new [`TypeDefParameters`] instance.
pub fn new(params: Vec<TypeParameter>) -> Self {
let unused = params.iter().cloned().collect();
Self { params, unused }
}
/// Update the set of unused type parameters by removing those that are used in the given
/// fields.
pub fn update_unused<'a>(&mut self, fields: impl Iterator<Item = &'a CompositeDefFieldType>) {
let mut used_type_params = BTreeSet::new();
for field in fields {
field.type_path.parent_type_params(&mut used_type_params)
}
for used_type_param in &used_type_params {
self.unused.remove(used_type_param);
}
}
/// Construct a [`core::marker::PhantomData`] for the type unused type params.
pub fn unused_params_phantom_data(&self) -> Option<syn::TypePath> {
if self.unused.is_empty() {
return None;
}
let params = if self.unused.len() == 1 {
let param = self
.unused
.iter()
.next()
.expect("Checked for exactly one unused param");
quote! { #param }
} else {
let params = self.unused.iter();
quote! { ( #( #params ), * ) }
};
Some(syn::parse_quote! { ::core::marker::PhantomData<#params> })
}
/// Returns the set of type parameters.
pub fn params(&self) -> &[TypeParameter] {
&self.params
}
/// Returns true if there are any unused type params
pub fn has_unused_type_params(&self) -> bool {
!self.unused.is_empty()
}
}
impl quote::ToTokens for TypeDefParameters {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
if !self.params.is_empty() {
let params = &self.params;
tokens.extend(quote! { < #( #params ),* > })
}
}
}
-304
View File
@@ -1,304 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
use scale_info::{form::PortableForm, Path, TypeDefPrimitive};
use std::collections::BTreeSet;
use syn::parse_quote;
/// An opaque struct representing a type path. The main usage of this is
/// to spit out as tokens in some `quote!{ ... }` macro; the inner structure
/// should be unimportant.
#[derive(Clone, Debug)]
pub struct TypePath(TypePathInner);
#[derive(Clone, Debug)]
pub enum TypePathInner {
Parameter(TypeParameter),
Type(TypePathType),
}
impl quote::ToTokens for TypePath {
fn to_tokens(&self, tokens: &mut TokenStream) {
let syn_type = self.to_syn_type();
syn_type.to_tokens(tokens)
}
}
impl TypePath {
/// Construct a [`TypePath`] from a [`TypeParameter`]
pub fn from_parameter(param: TypeParameter) -> TypePath {
TypePath(TypePathInner::Parameter(param))
}
/// Construct a [`TypePath`] from a [`TypeParameter`]
pub fn from_type(ty: TypePathType) -> TypePath {
TypePath(TypePathInner::Type(ty))
}
/// Construct a [`TypePath`] from a [`syn::TypePath`]
pub fn from_syn_path(path: syn::Path) -> TypePath {
// Note; this doesn't parse the parameters or anything, but since nothing external
// can inspect this structure, and the ToTokens impl works either way, it should be ok.
TypePath(TypePathInner::Type(TypePathType::Path {
path,
params: Vec::new(),
}))
}
pub(crate) fn to_syn_type(&self) -> syn::Type {
match &self.0 {
TypePathInner::Parameter(ty_param) => syn::Type::Path(parse_quote! { #ty_param }),
TypePathInner::Type(ty) => ty.to_syn_type(),
}
}
pub(crate) fn is_compact(&self) -> bool {
matches!(&self.0, TypePathInner::Type(ty) if ty.is_compact())
}
pub(crate) fn is_string(&self) -> bool {
matches!(&self.0, TypePathInner::Type(ty) if ty.is_string())
}
/// Returns the type parameters in a path which are inherited from the containing type.
///
/// # Example
///
/// ```rust
/// struct S<T> {
/// a: Vec<Option<T>>, // the parent type param here is `T`
/// }
/// ```
pub fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
match &self.0 {
TypePathInner::Parameter(type_parameter) => {
acc.insert(type_parameter.clone());
}
TypePathInner::Type(type_path) => type_path.parent_type_params(acc),
}
}
/// Gets the vector type parameter if the data is represented as `TypeDef::Sequence`.
///
/// **Note:** Utilized for transforming `std::vec::Vec<T>` into slices `&[T]` for the storage API.
pub fn vec_type_param(&self) -> Option<&TypePath> {
let ty = match &self.0 {
TypePathInner::Type(ty) => ty,
_ => return None,
};
match ty {
TypePathType::Vec { of } => Some(of),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub enum TypePathType {
Path {
path: syn::Path,
params: Vec<TypePath>,
},
Vec {
of: Box<TypePath>,
},
Array {
len: usize,
of: Box<TypePath>,
},
Tuple {
elements: Vec<TypePath>,
},
Primitive {
def: TypeDefPrimitive,
},
Compact {
inner: Box<TypePath>,
is_field: bool,
crate_path: syn::Path,
},
BitVec {
bit_order_type: Box<TypePath>,
bit_store_type: Box<TypePath>,
crate_path: syn::Path,
},
}
impl TypePathType {
pub fn from_type_def_path(
path: &Path<PortableForm>,
root_mod_ident: Ident,
params: Vec<TypePath>,
) -> Self {
let path_segments = &*path.segments;
let path: syn::Path = match path_segments {
[] => panic!("Type has no ident"),
[ident] => {
// paths to prelude types
match ident.as_str() {
"Option" => parse_quote!(::core::option::Option),
"Result" => parse_quote!(::core::result::Result),
"Cow" => parse_quote!(::std::borrow::Cow),
"BTreeMap" => parse_quote!(::std::collections::BTreeMap),
"BTreeSet" => parse_quote!(::std::collections::BTreeSet),
"Range" => parse_quote!(::core::ops::Range),
"RangeInclusive" => parse_quote!(::core::ops::RangeInclusive),
"NonZeroI8" => parse_quote!(::core::num::NonZeroI8),
"NonZeroU8" => parse_quote!(::core::num::NonZeroU8),
"NonZeroI16" => parse_quote!(::core::num::NonZeroI16),
"NonZeroU16" => parse_quote!(::core::num::NonZeroU16),
"NonZeroI32" => parse_quote!(::core::num::NonZeroI32),
"NonZeroU32" => parse_quote!(::core::num::NonZeroU32),
"NonZeroI64" => parse_quote!(::core::num::NonZeroI64),
"NonZeroU64" => parse_quote!(::core::num::NonZeroU64),
"NonZeroI128" => parse_quote!(::core::num::NonZeroI128),
"NonZeroU128" => parse_quote!(::core::num::NonZeroU128),
"NonZeroIsize" => parse_quote!(::core::num::NonZeroIsize),
"NonZeroUsize" => parse_quote!(::core::num::NonZeroUsize),
ident => panic!("Unknown prelude type '{ident}'"),
}
}
_ => {
// paths to generated types in the root types module
let mut ty_path = path_segments
.iter()
.map(|s| syn::PathSegment::from(format_ident!("{}", s)))
.collect::<syn::punctuated::Punctuated<syn::PathSegment, syn::Token![::]>>();
ty_path.insert(0, syn::PathSegment::from(root_mod_ident));
parse_quote!( #ty_path )
}
};
Self::Path { path, params }
}
/// Visits a type path, collecting all the generic type parameters from the containing type.
///
/// # Example
///
/// ```rust
/// struct S<T> {
/// a: Vec<Option<T>>, // the parent type param here is `T`
/// }
/// ```
fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
match self {
TypePathType::Path { params, .. } => {
for p in params {
p.parent_type_params(acc)
}
}
TypePathType::Vec { of } => of.parent_type_params(acc),
TypePathType::Array { of, .. } => of.parent_type_params(acc),
TypePathType::Tuple { elements } => {
for e in elements {
e.parent_type_params(acc)
}
}
TypePathType::Primitive { .. } => (),
TypePathType::Compact { inner, .. } => inner.parent_type_params(acc),
TypePathType::BitVec {
bit_order_type,
bit_store_type,
crate_path: _,
} => {
bit_order_type.parent_type_params(acc);
bit_store_type.parent_type_params(acc);
}
}
}
pub(crate) fn is_compact(&self) -> bool {
matches!(self, TypePathType::Compact { .. })
}
pub(crate) fn is_string(&self) -> bool {
matches!(
self,
TypePathType::Primitive {
def: TypeDefPrimitive::Str
}
)
}
fn to_syn_type(&self) -> syn::Type {
match &self {
TypePathType::Path { path, params } => {
let path = if params.is_empty() {
parse_quote! { #path }
} else {
parse_quote! { #path< #( #params ),* > }
};
syn::Type::Path(path)
}
TypePathType::Vec { of } => {
let type_path = parse_quote! { ::std::vec::Vec<#of> };
syn::Type::Path(type_path)
}
TypePathType::Array { len, of } => {
let array = parse_quote! { [#of; #len] };
syn::Type::Array(array)
}
TypePathType::Tuple { elements } => {
let tuple = parse_quote! { (#( # elements, )* ) };
syn::Type::Tuple(tuple)
}
TypePathType::Primitive { def } => syn::Type::Path(match def {
TypeDefPrimitive::Bool => parse_quote!(::core::primitive::bool),
TypeDefPrimitive::Char => parse_quote!(::core::primitive::char),
TypeDefPrimitive::Str => parse_quote!(::std::string::String),
TypeDefPrimitive::U8 => parse_quote!(::core::primitive::u8),
TypeDefPrimitive::U16 => parse_quote!(::core::primitive::u16),
TypeDefPrimitive::U32 => parse_quote!(::core::primitive::u32),
TypeDefPrimitive::U64 => parse_quote!(::core::primitive::u64),
TypeDefPrimitive::U128 => parse_quote!(::core::primitive::u128),
TypeDefPrimitive::U256 => unimplemented!("not a rust primitive"),
TypeDefPrimitive::I8 => parse_quote!(::core::primitive::i8),
TypeDefPrimitive::I16 => parse_quote!(::core::primitive::i16),
TypeDefPrimitive::I32 => parse_quote!(::core::primitive::i32),
TypeDefPrimitive::I64 => parse_quote!(::core::primitive::i64),
TypeDefPrimitive::I128 => parse_quote!(::core::primitive::i128),
TypeDefPrimitive::I256 => unimplemented!("not a rust primitive"),
}),
TypePathType::Compact {
inner,
is_field,
crate_path,
} => {
let path = if *is_field {
// compact fields can use the inner compact type directly and be annotated with
// the `compact` attribute e.g. `#[codec(compact)] my_compact_field: u128`
parse_quote! ( #inner )
} else {
parse_quote! ( #crate_path::ext::codec::Compact<#inner> )
};
syn::Type::Path(path)
}
TypePathType::BitVec {
bit_order_type,
bit_store_type,
crate_path,
} => {
let type_path = parse_quote! { #crate_path::utils::bits::DecodedBits<#bit_store_type, #bit_order_type> };
syn::Type::Path(type_path)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TypeParameter {
pub(super) concrete_type_id: u32,
pub(super) original_name: String,
pub(super) name: Ident,
}
impl quote::ToTokens for TypeParameter {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.name.to_tokens(tokens)
}
}
+6 -2
View File
@@ -61,12 +61,16 @@ struct RuntimeMetadataArgs {
struct DeriveForType {
path: syn::TypePath,
derive: Punctuated<syn::Path, syn::Token![,]>,
#[darling(default)]
recursive: bool,
}
#[derive(Debug, FromMeta)]
struct AttributesForType {
path: syn::TypePath,
attributes: Punctuated<OuterAttribute, syn::Token![,]>,
#[darling(default)]
recursive: bool,
}
#[derive(Debug, FromMeta)]
@@ -123,7 +127,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
.collect(),
);
for d in args.derive_for_type {
codegen.add_derives_for_type(d.path, d.derive.into_iter());
codegen.add_derives_for_type(d.path, d.derive.into_iter(), d.recursive);
}
// Configure attributes:
@@ -135,7 +139,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
.collect(),
);
for d in args.attributes_for_type {
codegen.add_attributes_for_type(d.path, d.attributes.into_iter().map(|a| a.0))
codegen.add_attributes_for_type(d.path, d.attributes.into_iter().map(|a| a.0), d.recursive)
}
// Insert type substitutions:
+3 -8
View File
@@ -6,15 +6,10 @@ use subxt_signer::sr25519::dev;
#[subxt::subxt(
runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale",
derive_for_type(path = "xcm::v2::multilocation::MultiLocation", derive = "Clone"),
derive_for_type(path = "xcm::v2::multilocation::Junctions", derive = "Clone"),
derive_for_type(path = "xcm::v2::junction::Junction", derive = "Clone"),
derive_for_type(path = "xcm::v2::NetworkId", derive = "Clone"),
derive_for_type(path = "xcm::v2::BodyId", derive = "Clone"),
derive_for_type(path = "xcm::v2::BodyPart", derive = "Clone"),
derive_for_type(
path = "bounded_collections::weak_bounded_vec::WeakBoundedVec",
derive = "Clone"
path = "xcm::v2::multilocation::MultiLocation",
derive = "Clone",
recursive
)
)]
pub mod runtime {}
+1 -1
View File
@@ -213,7 +213,7 @@ impl ModuleError {
/// Details about the module error.
pub struct ModuleErrorDetails<'a> {
/// The pallet that the error came from
/// The pallet that the error is in
pub pallet: crate::metadata::types::PalletMetadata<'a>,
/// The variant representing the error
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
+1 -1
View File
@@ -107,7 +107,7 @@ pub mod ext {
/// ```
///
/// The `subxt` macro will populate the annotated module with all of the methods and types required
/// for interacting with the runtime that the metadata came from via Subxt.
/// for interacting with the runtime that the metadata is in via Subxt.
///
/// # Configuration
///
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
error: Type substitution error: `substitute_type(with = <path>)` must be a path prefixed with 'crate::' or '::'
error: Type Generation failed: Type substitution error: `substitute_type(with = <path>)` must be a path prefixed with 'crate::' or '::'
--> src/incorrect/substitute_path_not_absolute.rs:5:16
|
5 | with = "sp_runtime::Perbill"