From cff2577eee2573b6722eefb84e57a86779a28a6f Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 26 Oct 2022 19:43:41 -0700 Subject: [PATCH 1/2] Refactor RustType (Conceptual / Serialization) Before `RustType` represented how things are serialized e.g. `Tagged`, `CBORBytes`, etc as well as the underlying rust type we use, despite in 90% of cases us only caring about the underlying rust type. This is now split up into several structs with `RustType` being the complete type with both components and `ConceptualRustType` being the underlying rust type used outside of serialization contexts. This is work done ahead of supporting `.default` specs as that would have necessitated an additional `RustType::Default(FixedValue, Box)` variant otherwise, further adding more serialization-only information, causing potential bugs when not properly dealt with. --- Cargo.lock | 269 ++++++++++++------- src/generation.rs | 420 +++++++++++++++-------------- src/intermediate.rs | 632 +++++++++++++++++++++++--------------------- src/parsing.rs | 112 ++++---- 4 files changed, 784 insertions(+), 649 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0742039..7127b7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64-url" @@ -94,18 +94,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "cbor_event" @@ -113,10 +113,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b3d8b289b6c7d6d8832c8e2bf151c7677126afa627f51e19a6320aec8237cb" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cddl" version = "0.9.0" -source = "git+https://github.com/anweiss/cddl?branch=dcSpark-ast-parent-pointer#a94078e041023052c6c8d4b8786c6267a0696c1a" +source = "git+https://github.com/anweiss/cddl?branch=dcSpark-ast-parent-pointer#2c271dd78f9d1fd1c15e8ed42dd53090f930f689" dependencies = [ "abnf_to_pest", "base16", @@ -282,9 +288,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -324,11 +330,55 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", @@ -402,18 +452,28 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "iana-time-zone" -version = "0.1.47" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", - "once_cell", "wasm-bindgen", "winapi", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -426,24 +486,24 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -520,15 +580,24 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.132" +version = "0.2.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "55edcf6c0bb319052dea84732cf99db461780fd5e8d3eb46ab6ff312ab31f197" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -557,9 +626,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", @@ -598,9 +667,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "os_str_bytes" @@ -620,9 +689,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", @@ -633,9 +702,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ "thiserror", "ucd-trie", @@ -643,20 +712,20 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04" +checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" dependencies = [ "once_cell", "pest", - "sha-1", + "sha1", ] [[package]] name = "pest_vm" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16f91ae87631532461c733bf48e0a988056b23bf1ecba6cdee0bf72b623e5d5" +checksum = "266589a43e1351b2233db0e1438414f752c9af38fea696715a78739a2454a6dc" dependencies = [ "pest", "pest_meta", @@ -700,9 +769,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -752,20 +821,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "serde" -version = "1.0.144" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] [[package]] name = "serde-wasm-bindgen" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" dependencies = [ "js-sys", "serde", @@ -774,9 +849,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", @@ -785,9 +860,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa", "ryu", @@ -795,10 +870,10 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.10.0" +name = "sha1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", @@ -848,9 +923,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "static_assertions" @@ -866,9 +941,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -886,24 +961,24 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thiserror" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -935,27 +1010,27 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "uriparse" @@ -987,9 +1062,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -997,9 +1072,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", @@ -1012,9 +1087,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1022,9 +1097,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -1035,9 +1110,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "winapi" @@ -1072,43 +1147,57 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/src/generation.rs b/src/generation.rs index f027069..faf1768 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -6,7 +6,9 @@ use crate::cli::CLI_ARGS; use crate::intermediate::{ AliasIdent, + CBOREncodingOperation, CDDLIdent, + ConceptualRustType, EnumVariant, FixedValue, IntermediateTypes, @@ -135,6 +137,27 @@ fn concat_files(paths: Vec<&str>) -> std::io::Result { Ok(buf) } +enum SerializingRustType<'a> { + EncodingOperation(&'a CBOREncodingOperation, Box>), + Root(&'a ConceptualRustType), +} + +impl <'a> std::convert::From<&'a RustType> for SerializingRustType<'a> { + fn from(rust_type: &'a RustType) -> Self { + let mut root = Self::Root(&rust_type.conceptual_type); + for cbor_encoding_op in rust_type.encodings.iter() { + root = Self::EncodingOperation(cbor_encoding_op, Box::new(root)); + } + root + } +} + +impl <'a> std::convert::From<&'a ConceptualRustType> for SerializingRustType<'a> { + fn from(conceptual_rust_type: &'a ConceptualRustType) -> Self { + Self::Root(conceptual_rust_type) + } +} + pub struct GenerationScope { rust_lib_scope: codegen::Scope, rust_scopes: BTreeMap, @@ -227,7 +250,7 @@ impl GenerationScope { .raw(&format!("pub type {} = {};", ident, base_type.for_rust_member(false))); } if *gen_wasm_alias { - if let RustType::Fixed(constant) = base_type { + if let ConceptualRustType::Fixed(constant) = &base_type.conceptual_type { // wasm-bindgen doesn't support const or static vars so we must do a function let (ty, val) = match constant { FixedValue::Null => panic!("null constants not supported"), @@ -259,7 +282,7 @@ impl GenerationScope { if CLI_ARGS.wasm { rust_struct.visit_types(types, &mut |ty| { match ty { - RustType::Array(elem) => { + ConceptualRustType::Array(elem) => { if !ty.directly_wasm_exposable() { let array_ident = elem.name_as_wasm_array(); if wasm_wrappers_generated.insert(array_ident.clone()) { @@ -267,12 +290,12 @@ impl GenerationScope { } } }, - RustType::Map(k, v) => { - let map_ident = RustType::name_for_wasm_map(&k, &v); + ConceptualRustType::Map(k, v) => { + let map_ident = ConceptualRustType::name_for_wasm_map(&k, &v); if wasm_wrappers_generated.insert(map_ident.to_string()) { codegen_table_type(self, types, &map_ident, *k.clone(), *v.clone(), None); } - if !RustType::Array(Box::new(*k.clone())).directly_wasm_exposable() { + if !ConceptualRustType::Array(Box::new(*k.clone())).directly_wasm_exposable() { let keys_ident = k.name_as_wasm_array(); if wasm_wrappers_generated.insert(keys_ident.clone()) { self.generate_array_type(types, *k.clone(), &RustIdent::new(CDDLIdent::new(keys_ident))); @@ -289,7 +312,7 @@ impl GenerationScope { }, RustStructType::Table { domain, range } => { if CLI_ARGS.wasm { - let map_ident = RustType::name_for_wasm_map(domain, range); + let map_ident = ConceptualRustType::name_for_wasm_map(domain, range); if wasm_wrappers_generated.insert(map_ident.to_string()) { codegen_table_type(self, types, rust_ident, domain.clone(), range.clone(), rust_struct.tag()); } else { @@ -298,7 +321,7 @@ impl GenerationScope { } //self // .rust() - // .raw(&format!("type {} = {};", rust_struct.ident(), RustType::name_for_rust_map(domain, range, false))); + // .raw(&format!("type {} = {};", rust_struct.ident(), ConceptualRustType::name_for_rust_map(domain, range, false))); }, RustStructType::Array { element_type } => { if CLI_ARGS.wasm { @@ -316,7 +339,7 @@ impl GenerationScope { }, RustStructType::Wrapper{ wrapped, min_max } => { match rust_struct.tag() { - Some(tag) => generate_wrapper_struct(self, types, rust_ident, &RustType::Tagged(tag, Box::new(wrapped.clone())), min_max.clone()), + Some(tag) => generate_wrapper_struct(self, types, rust_ident, &wrapped.clone().tag(tag), min_max.clone()), None => generate_wrapper_struct(self, types, rust_ident, wrapped, min_max.clone()), } }, @@ -542,7 +565,7 @@ impl GenerationScope { // expr - the name of the variable where this is accessed, e.g. "self.foo" or "field" (e.g. for if let Some(field) = self.foo) // var_name - For use in generating *unique* identifiers from this. Must be unique within a type, e.g. field name: for the above it would be "foo" for both // serializer_name_overload will provide an overload instead of using "serializer". (name, is_local) - if is_local then &mut will be appended when needed. - fn generate_serialize(&mut self, types: &IntermediateTypes, rust_type: &RustType, body: &mut dyn CodeBlock, config: SerializeConfig) { + fn generate_serialize<'a>(&mut self, types: &IntermediateTypes, serializing_rust_type: SerializingRustType<'a>, body: &mut dyn CodeBlock, config: SerializeConfig) { //body.line(&format!("// DEBUG - generated from: {:?}", rust_type)); let line_ender = if config.is_end { "" @@ -569,8 +592,24 @@ impl GenerationScope { }; let encoding_var = config.encoding_var(None); let encoding_var_deref = format!("{}{}", encoding_deref, encoding_var); - match rust_type { - RustType::Fixed(value) => match value { + match serializing_rust_type { + SerializingRustType::EncodingOperation(CBOREncodingOperation::Tagged(tag), child) => { + let expr = format!("{}u64", tag); + write_using_sz(body, "write_tag", serializer_use, &expr, &expr, "?;", &format!("{}{}", encoding_deref, config.encoding_var(Some("tag")))); + self.generate_serialize(types, *child, body, config); + }, + SerializingRustType::EncodingOperation(CBOREncodingOperation::CBORBytes, child) => { + let inner_se = format!("{}_inner_se", config.var_name); + body.line(&format!("let mut {} = Serializer::new_vec();", inner_se)); + let inner_config = config + .clone() + .is_end(false) + .serializer_name_overload((&inner_se, true)); + self.generate_serialize(types, *child, body, inner_config); + body.line(&format!("let {}_bytes = {}.finalize();", config.var_name, inner_se)); + write_string_sz(body, "write_bytes", serializer_use, &format!("{}_bytes", config.var_name), line_ender, &config.encoding_var(Some("bytes"))); + }, + SerializingRustType::Root(ConceptualRustType::Fixed(value)) => match value { FixedValue::Null => { body.line(&format!("{}.write_special(CBORSpecial::Null){}", serializer_use, line_ender)); }, @@ -608,7 +647,7 @@ impl GenerationScope { write_string_sz(body, "write_text", serializer_use, &format!("\"{}\"", s), line_ender, &encoding_var); }, }, - RustType::Primitive(primitive) => match primitive { + SerializingRustType::Root(ConceptualRustType::Primitive(primitive)) => match primitive { Primitive::Bool => { body.line(&format!("{}.write_special(cbor_event::Special::Bool({})){}", serializer_use, expr_deref, line_ender)); }, @@ -663,18 +702,18 @@ impl GenerationScope { } }, }, - RustType::Rust(t) => { + SerializingRustType::Root(ConceptualRustType::Rust(t)) => { if types.is_plain_group(t) { body.line(&format!("{}.serialize_as_embedded_group({}{}){}", config.expr, serializer_pass, canonical_param(), line_ender)); } else { body.line(&format!("{}.serialize({}{}){}", config.expr, serializer_pass, canonical_param(), line_ender)); } }, - RustType::Array(ty) => { + SerializingRustType::Root(ConceptualRustType::Array(ty)) => { start_len(body, Representation::Array, serializer_use, &encoding_var, &format!("{}.len() as u64", config.expr)); let elem_var_name = format!("{}_elem", config.var_name); let elem_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&elem_var_name, &ty.clone().resolve_aliases()) + encoding_fields(&elem_var_name, (&ty.clone().resolve_aliases()).into()) } else { vec![] }; @@ -693,15 +732,15 @@ impl GenerationScope { .is_end(false) .encoding_var_no_option_struct() .encoding_var_is_ref(false); - self.generate_serialize(types, ty, &mut loop_block, elem_config); + self.generate_serialize(types, (&**ty).into(), &mut loop_block, elem_config); body.push_block(loop_block); end_len(body, serializer_use, &encoding_var, config.is_end); }, - RustType::Map(key, value) => { + SerializingRustType::Root(ConceptualRustType::Map(key, value)) => { start_len(body, Representation::Map, serializer_use, &encoding_var, &format!("{}.len() as u64", config.expr)); let ser_loop = if CLI_ARGS.preserve_encodings { - let key_enc_fields = encoding_fields(&format!("{}_key", config.var_name), &key.clone().resolve_aliases()); - let value_enc_fields = encoding_fields(&format!("{}_value", config.var_name), &value.clone().resolve_aliases()); + let key_enc_fields = encoding_fields(&format!("{}_key", config.var_name), (&key.clone().resolve_aliases()).into()); + let value_enc_fields = encoding_fields(&format!("{}_value", config.var_name), (&value.clone().resolve_aliases()).into()); let mut ser_loop = if CLI_ARGS.canonical_form { let mut key_order = codegen::Block::new(&format!("let mut key_order = {}.iter().map(|(k, v)|", config.expr)); key_order @@ -714,7 +753,7 @@ impl GenerationScope { .is_end(false) .serializer_name_overload(("buf", true)) .encoding_var_is_ref(false); - self.generate_serialize(types, key, &mut key_order, key_config); + self.generate_serialize(types, (&**key).into(), &mut key_order, key_config); key_order .line("Ok((buf.finalize(), k, v))") .after(").collect::, &_, &_)>, cbor_event::Error>>()?;"); @@ -747,7 +786,7 @@ impl GenerationScope { .is_end(false) .encoding_var_no_option_struct() .encoding_var_is_ref(false); - self.generate_serialize(types, key, &mut ser_loop, key_config); + self.generate_serialize(types, (&**key).into(), &mut ser_loop, key_config); ser_loop }; if !value_enc_fields.is_empty() { @@ -761,7 +800,7 @@ impl GenerationScope { .is_end(false) .encoding_var_no_option_struct() .encoding_var_is_ref(false); - self.generate_serialize(types, value, &mut ser_loop, value_config); + self.generate_serialize(types, (&**value).into(), &mut ser_loop, value_config); ser_loop } else { let mut ser_loop = Block::new(&format!("for (key, value) in {}.iter()", config.expr)); @@ -777,19 +816,14 @@ impl GenerationScope { .clone() .expr("value") .var_name(format!("{}_value", config.var_name)); - self.generate_serialize(types, key, &mut ser_loop, key_config); - self.generate_serialize(types, value, &mut ser_loop, value_config); + self.generate_serialize(types, (&**key).into(), &mut ser_loop, key_config); + self.generate_serialize(types, (&**value).into(), &mut ser_loop, value_config); ser_loop }; body.push_block(ser_loop); end_len(body, serializer_use, &encoding_var, config.is_end); }, - RustType::Tagged(tag, ty) => { - let expr = format!("{}u64", tag); - write_using_sz(body, "write_tag", serializer_use, &expr, &expr, "?;", &format!("{}{}", encoding_deref, config.encoding_var(Some("tag")))); - self.generate_serialize(types, ty, body, config); - }, - RustType::Optional(ty) => { + SerializingRustType::Root(ConceptualRustType::Optional(ty)) => { let mut opt_block = Block::new(&format!("match {}", expr_ref)); // TODO: do this in one line without a block if possible somehow. // see other comment in generate_enum() @@ -799,7 +833,7 @@ impl GenerationScope { .expr("x") .expr_is_ref(true) .is_end(true); - self.generate_serialize(types, ty, &mut some_block, opt_config); + self.generate_serialize(types, (&**ty).into(), &mut some_block, opt_config); some_block.after(","); opt_block.push_block(some_block); opt_block.line(&format!("None => {}.write_special(CBORSpecial::Null),", serializer_use)); @@ -808,18 +842,7 @@ impl GenerationScope { } body.push_block(opt_block); }, - RustType::Alias(_ident, ty) => self.generate_serialize(types, ty, body, config), - RustType::CBORBytes(ty) => { - let inner_se = format!("{}_inner_se", config.var_name); - body.line(&format!("let mut {} = Serializer::new_vec();", inner_se)); - let inner_config = config - .clone() - .is_end(false) - .serializer_name_overload((&inner_se, true)); - self.generate_serialize(types, ty, body, inner_config); - body.line(&format!("let {}_bytes = {}.finalize();", config.var_name, inner_se)); - write_string_sz(body, "write_bytes", serializer_use, &format!("{}_bytes", config.var_name), line_ender, &config.encoding_var(Some("bytes"))); - }, + SerializingRustType::Root(ConceptualRustType::Alias(_ident, ty)) => self.generate_serialize(types, (&**ty).into(), body, config), }; } @@ -831,7 +854,7 @@ impl GenerationScope { // var_name is passed in for use in creating unique identifiers for temporaries // if force_non_embedded always deserialize as the outer wrapper, not as the embedded plain group when the Rust ident is for a plain group // final_exprs contains what other expressions to return as part of the final tuple e.g. (x, x_encoding, x_key_encodings) - fn generate_deserialize(&mut self, types: &IntermediateTypes, rust_type: &RustType, var_name: &str, before: &str, after: &str, in_embedded: bool, optional_field: bool, mut final_exprs: Vec, body: &mut dyn CodeBlock, deserializer_name_overload: Option<&str>) { + fn generate_deserialize(&mut self, types: &IntermediateTypes, serializing_rust_type: SerializingRustType, var_name: &str, before: &str, after: &str, in_embedded: bool, optional_field: bool, mut final_exprs: Vec, body: &mut dyn CodeBlock, deserializer_name_overload: Option<&str>) { let deserializer_name = deserializer_name_overload.unwrap_or("raw"); //body.line(&format!("println!(\"deserializing {}\");", var_name)); if !CLI_ARGS.preserve_encodings { @@ -848,8 +871,8 @@ impl GenerationScope { encoding_exprs.join(", ") } }; - match rust_type { - RustType::Fixed(f) => { + match serializing_rust_type { + SerializingRustType::Root(ConceptualRustType::Fixed(f)) => { if !CLI_ARGS.preserve_encodings { // we don't evaluate to any values here, just verify // before/after are ignored and we need to handle fixed value deserialization in a different way @@ -918,7 +941,7 @@ impl GenerationScope { _ => unimplemented!(), }; }, - RustType::Primitive(p) => { + SerializingRustType::Root(ConceptualRustType::Primitive(p)) => { if optional_field { body.line("read_len.read_elems(1)?;"); } @@ -996,7 +1019,7 @@ impl GenerationScope { }, }; }, - RustType::Rust(ident) => if types.is_plain_group(ident) { + SerializingRustType::Root(ConceptualRustType::Rust(ident)) => if types.is_plain_group(ident) { // This would mess up with length checks otherwise and is probably not a likely situation if this is even valid in CDDL. // To have this work (if it's valid) you'd either need to generate 2 embedded deserialize methods or pass // a parameter whether it was an optional field, and if so, read_len.read_elems(embedded mandatory fields)?; @@ -1014,31 +1037,7 @@ impl GenerationScope { } body.line(&format!("{}{}{}", before, final_expr(final_exprs, Some(format!("{}::deserialize({})?", ident, deserializer_name))), after)); }, - RustType::Tagged(tag, ty) => { - if optional_field { - body.line("read_len.read_elems(1)?;"); - } - let mut tag_check = if CLI_ARGS.preserve_encodings { - let mut tag_check = Block::new(&format!("{}match {}.tag_sz()?", before, deserializer_name)); - let mut deser_block = Block::new(&format!("({}, tag_enc) =>", tag)); - final_exprs.push("Some(tag_enc)".to_owned()); - self.generate_deserialize(types, ty, var_name, "", "", in_embedded, false, final_exprs, &mut deser_block, deserializer_name_overload); - deser_block.after(","); - tag_check.push_block(deser_block); - tag_check - } else { - let mut tag_check = Block::new(&format!("{}match {}.tag()?", before, deserializer_name)); - let mut deser_block = Block::new(&format!("{} =>", tag)); - self.generate_deserialize(types, ty, var_name, "", "", in_embedded, false, final_exprs, &mut deser_block, deserializer_name_overload); - deser_block.after(","); - tag_check.push_block(deser_block); - tag_check - }; - tag_check.line(&format!("{} => return Err(DeserializeFailure::TagMismatch{{ found: tag, expected: {} }}.into()),", if CLI_ARGS.preserve_encodings { "(tag, _enc)" } else { "tag" }, tag)); - tag_check.after(after); - body.push_block(tag_check); - }, - RustType::Optional(ty) => { + SerializingRustType::Root(ConceptualRustType::Optional(ty)) => { let read_len_check = optional_field || (ty.expanded_field_count(types) != Some(1)); // codegen crate doesn't support if/else or appending a block after a block, only strings // so we need to create a local bool var and use a match instead @@ -1075,12 +1074,12 @@ impl GenerationScope { } } let ty_enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(var_name, &ty.clone().resolve_aliases()) + encoding_fields(var_name, (&ty.clone().resolve_aliases()).into()) } else { vec![] }; if ty_enc_fields.is_empty() { - self.generate_deserialize(types, ty, var_name, "Some(", ")", in_embedded, false, final_exprs, &mut some_block, deserializer_name_overload); + self.generate_deserialize(types, (&**ty).into(), var_name, "Some(", ")", in_embedded, false, final_exprs, &mut some_block, deserializer_name_overload); } else { let (map_some_before, map_some_after) = if ty.is_fixed_value() { // case 1: no actual return, only encoding values for tags/fixed values, no need to wrap in Some() @@ -1095,7 +1094,7 @@ impl GenerationScope { // we need to annotate the Ok's error type since the compiler gets confused otherwise ("Result::<_, DeserializeError>::Ok(", format!(").map(|(x, {})| (Some(x), {}))?", enc_vars_str, enc_vars_str)) }; - self.generate_deserialize(types, ty, var_name, map_some_before, &map_some_after, in_embedded, false, final_exprs, &mut some_block, deserializer_name_overload); + self.generate_deserialize(types, (&**ty).into(), var_name, map_some_before, &map_some_after, in_embedded, false, final_exprs, &mut some_block, deserializer_name_overload); } some_block.after(","); deser_block.push_block(some_block); @@ -1104,7 +1103,7 @@ impl GenerationScope { none_block.line("read_len.read_elems(1)?;"); } // we don't use this to avoid the new (true) if CLI_ARGS.preserve_encodings is set - //self.generate_deserialize(types, &RustType::Fixed(FixedValue::Null), var_name, "", "", in_embedded, false, add_parens, &mut none_block); + //self.generate_deserialize(types, &ConceptualRustType::Fixed(FixedValue::Null), var_name, "", "", in_embedded, false, add_parens, &mut none_block); let mut check_null = Block::new(&format!("if {}.special()? != CBORSpecial::Null", deserializer_name)); check_null.line("return Err(DeserializeFailure::ExpectedNull.into());"); none_block.push_block(check_null); @@ -1129,7 +1128,7 @@ impl GenerationScope { deser_block.push_block(none_block); body.push_block(deser_block); }, - RustType::Array(ty) => { + SerializingRustType::Root(ConceptualRustType::Array(ty)) => { //if !self.deserialize_generated_for_type(&element_type) { // TODO: check this elsehwere??? //self.dont_generate_deserialize(&array_type_ident, format!("inner type {} doesn't support deserialize", element_type.for_rust_member())); @@ -1141,7 +1140,7 @@ impl GenerationScope { body.line(&format!("let mut {} = Vec::new();", arr_var_name)); let elem_var_name = format!("{}_elem", var_name); let elem_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&elem_var_name, &ty.clone().resolve_aliases()) + encoding_fields(&elem_var_name, (&ty.clone().resolve_aliases()).into()) } else { vec![] }; @@ -1157,13 +1156,13 @@ impl GenerationScope { } let mut deser_loop = make_deser_loop("len", &format!("{}.len()", arr_var_name)); deser_loop.push_block(make_deser_loop_break_check()); - if let RustType::Rust(ty_ident) = &**ty { + if let ConceptualRustType::Rust(ty_ident) = &ty.conceptual_type { // TODO: properly handle which read_len would be checked here. assert!(!types.is_plain_group(&*ty_ident)); } if !elem_encs.is_empty() { let elem_var_names_str = encoding_var_names_str(&elem_var_name, ty); - self.generate_deserialize(types, ty, &elem_var_name, &format!("let {} = ", elem_var_names_str), ";", in_embedded, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**ty).into(), &elem_var_name, &format!("let {} = ", elem_var_names_str), ";", in_embedded, false, vec![], &mut deser_loop, deserializer_name_overload); deser_loop .line(format!("{}.push({});", arr_var_name, elem_var_name)) .line(format!( @@ -1171,7 +1170,7 @@ impl GenerationScope { var_name, tuple_str(elem_encs.iter().map(|enc| enc.field_name.clone()).collect()))); } else { - self.generate_deserialize(types, ty, &elem_var_name, &format!("{}.push(", arr_var_name), ");", in_embedded, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**ty).into(), &elem_var_name, &format!("{}.push(", arr_var_name), ");", in_embedded, false, vec![], &mut deser_loop, deserializer_name_overload); } body.push_block(deser_loop); if CLI_ARGS.preserve_encodings { @@ -1184,15 +1183,15 @@ impl GenerationScope { body.line(&format!("{}{}{}", before, arr_var_name, after)); } }, - RustType::Map(key_type, value_type) => { + SerializingRustType::Root(ConceptualRustType::Map(key_type, value_type)) => { if optional_field { body.line("read_len.read_elems(1)?;"); } - if !self.deserialize_generated_for_type(key_type) { + if !self.deserialize_generated_for_type(&key_type.conceptual_type) { todo!(); // TODO: where is the best place to check for this? should we pass in a RustIdent to say where we're generating?! //self.dont_generate_deserialize(name, format!("key type {} doesn't support deserialize", key_type.for_rust_member())); - } else if !self.deserialize_generated_for_type(&value_type) { + } else if !self.deserialize_generated_for_type(&value_type.conceptual_type) { todo!(); //self.dont_generate_deserialize(name, format!("value type {} doesn't support deserialize", value_type.for_rust_member())); } else { @@ -1201,12 +1200,12 @@ impl GenerationScope { let key_var_name = format!("{}_key", var_name); let value_var_name = format!("{}_value", var_name); let key_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&key_var_name, &key_type.clone().resolve_aliases()) + encoding_fields(&key_var_name, (&key_type.clone().resolve_aliases()).into()) } else { vec![] }; let value_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&value_var_name, &value_type.clone().resolve_aliases()) + encoding_fields(&value_var_name, (&value_type.clone().resolve_aliases()).into()) } else { vec![] }; @@ -1229,19 +1228,19 @@ impl GenerationScope { if CLI_ARGS.preserve_encodings { let key_var_names_str = encoding_var_names_str(&key_var_name, key_type); let value_var_names_str = encoding_var_names_str(&value_var_name, value_type); - self.generate_deserialize(types, key_type, &key_var_name, &format!("let {} = ", key_var_names_str), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); - self.generate_deserialize(types, value_type, &value_var_name, &format!("let {} = ", value_var_names_str), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**key_type).into(), &key_var_name, &format!("let {} = ", key_var_names_str), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**value_type).into(), &value_var_name, &format!("let {} = ", value_var_names_str), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); } else { - self.generate_deserialize(types, key_type, &key_var_name, &format!("let {} = ", key_var_name), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); - self.generate_deserialize(types, value_type, &value_var_name, &format!("let {} = ", value_var_name), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**key_type).into(), &key_var_name, &format!("let {} = ", key_var_name), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); + self.generate_deserialize(types, (&**value_type).into(), &value_var_name, &format!("let {} = ", value_var_name), ";", false, false, vec![], &mut deser_loop, deserializer_name_overload); } let mut dup_check = Block::new(&format!("if {}.insert({}.clone(), {}).is_some()", table_var, key_var_name, value_var_name)); - let dup_key_error_key = match &**key_type { - RustType::Primitive(Primitive::U8) | - RustType::Primitive(Primitive::U16) | - RustType::Primitive(Primitive::U32) | - RustType::Primitive(Primitive::U64) => format!("Key::Uint({}.into())", key_var_name), - RustType::Primitive(Primitive::Str) => format!("Key::Str({})", key_var_name), + let dup_key_error_key = match &key_type.conceptual_type { + ConceptualRustType::Primitive(Primitive::U8) | + ConceptualRustType::Primitive(Primitive::U16) | + ConceptualRustType::Primitive(Primitive::U32) | + ConceptualRustType::Primitive(Primitive::U64) => format!("Key::Uint({}.into())", key_var_name), + ConceptualRustType::Primitive(Primitive::Str) => format!("Key::Str({})", key_var_name), // TODO: make a generic one then store serialized CBOR? _ => "Key::Str(String::from(\"some complicated/unsupported type\"))".to_owned(), }; @@ -1278,8 +1277,8 @@ impl GenerationScope { } } }, - RustType::Alias(_ident, ty) => self.generate_deserialize(types, ty, var_name, before, after, in_embedded, optional_field, final_exprs, body, deserializer_name_overload), - RustType::CBORBytes(ty) => { + SerializingRustType::Root(ConceptualRustType::Alias(_ident, ty)) => self.generate_deserialize(types, (&**ty).into(), var_name, before, after, in_embedded, optional_field, final_exprs, body, deserializer_name_overload), + SerializingRustType::EncodingOperation(CBOREncodingOperation::CBORBytes, child) => { if CLI_ARGS.preserve_encodings { final_exprs.push(format!("StringEncoding::from({}_bytes_encoding)", var_name)); body.line(&format!("let ({}_bytes, {}_bytes_encoding) = raw.bytes_sz()?;", var_name, var_name)); @@ -1288,7 +1287,31 @@ impl GenerationScope { }; let name_overload = "inner_de"; body.line(&format!("let mut {} = &mut Deserializer::from(std::io::Cursor::new({}_bytes));", name_overload, var_name)); - self.generate_deserialize(types, ty, var_name, before, after, in_embedded, optional_field, final_exprs, body, Some(name_overload)); + self.generate_deserialize(types, *child, var_name, before, after, in_embedded, optional_field, final_exprs, body, Some(name_overload)); + }, + SerializingRustType::EncodingOperation(CBOREncodingOperation::Tagged(tag), child) => { + if optional_field { + body.line("read_len.read_elems(1)?;"); + } + let mut tag_check = if CLI_ARGS.preserve_encodings { + let mut tag_check = Block::new(&format!("{}match {}.tag_sz()?", before, deserializer_name)); + let mut deser_block = Block::new(&format!("({}, tag_enc) =>", tag)); + final_exprs.push("Some(tag_enc)".to_owned()); + self.generate_deserialize(types, *child, var_name, "", "", in_embedded, false, final_exprs, &mut deser_block, deserializer_name_overload); + deser_block.after(","); + tag_check.push_block(deser_block); + tag_check + } else { + let mut tag_check = Block::new(&format!("{}match {}.tag()?", before, deserializer_name)); + let mut deser_block = Block::new(&format!("{} =>", tag)); + self.generate_deserialize(types, *child, var_name, "", "", in_embedded, false, final_exprs, &mut deser_block, deserializer_name_overload); + deser_block.after(","); + tag_check.push_block(deser_block); + tag_check + }; + tag_check.line(&format!("{} => return Err(DeserializeFailure::TagMismatch{{ found: tag, expected: {} }}.into()),", if CLI_ARGS.preserve_encodings { "(tag, _enc)" } else { "tag" }, tag)); + tag_check.after(after); + body.push_block(tag_check); }, } } @@ -1297,17 +1320,15 @@ impl GenerationScope { !self.no_deser_reasons.contains_key(name) } - fn deserialize_generated_for_type(&self, field_type: &RustType) -> bool { + fn deserialize_generated_for_type(&self, field_type: &ConceptualRustType) -> bool { match field_type { - RustType::Fixed(_) => true, - RustType::Primitive(_) => true, - RustType::Rust(ident) => self.deserialize_generated(ident), - RustType::Array(ty) => self.deserialize_generated_for_type(ty), - RustType::Map(k, v) => self.deserialize_generated_for_type(k) && self.deserialize_generated_for_type(v), - RustType::Tagged(_tag, ty) => self.deserialize_generated_for_type(ty), - RustType::Optional(ty) => self.deserialize_generated_for_type(ty), - RustType::Alias(_ident, ty) => self.deserialize_generated_for_type(ty), - RustType::CBORBytes(ty) => self.deserialize_generated_for_type(ty), + ConceptualRustType::Fixed(_) => true, + ConceptualRustType::Primitive(_) => true, + ConceptualRustType::Rust(ident) => self.deserialize_generated(ident), + ConceptualRustType::Array(ty) => self.deserialize_generated_for_type(&ty.conceptual_type), + ConceptualRustType::Map(k, v) => self.deserialize_generated_for_type(&k.conceptual_type) && self.deserialize_generated_for_type(&v.conceptual_type), + ConceptualRustType::Optional(ty) => self.deserialize_generated_for_type(&ty.conceptual_type), + ConceptualRustType::Alias(_ident, ty) => self.deserialize_generated_for_type(ty), } } @@ -1336,7 +1357,7 @@ impl GenerationScope { for variant in variants.iter() { let variant_arg = variant.name_as_var(); let enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(&variant_arg, &variant.rust_type.clone().resolve_aliases()) + encoding_fields(&variant_arg, (&variant.rust_type.clone().resolve_aliases()).into()) } else { vec![] }; @@ -1603,7 +1624,7 @@ fn create_base_rust_struct<'a>(types: &IntermediateTypes<'a>, ident: &RustIdent) let name = &ident.to_string(); let mut s = codegen::Struct::new(name); add_struct_derives(&mut s, types.used_as_key(ident), false); - let mut group_impl = codegen::Impl::new(name); + let group_impl = codegen::Impl::new(name); // TODO: anything here? (s, group_impl) } @@ -2013,7 +2034,7 @@ fn codegen_table_type(gen_scope: &mut GenerationScope, types: &IntermediateTypes // non-break special value once read assert!(!key_type.cbor_types().contains(&CBORType::Special)); let mut wrapper = create_base_wasm_struct(gen_scope, name, false); - let inner_type = RustType::name_for_rust_map(&key_type, &value_type, true); + let inner_type = ConceptualRustType::name_for_rust_map(&key_type, &value_type, true); wrapper.s.tuple_field(format!("pub(crate) {}", inner_type)); // new let mut new_func = codegen::Function::new("new"); @@ -2072,7 +2093,7 @@ fn codegen_table_type(gen_scope: &mut GenerationScope, types: &IntermediateTypes } wrapper.s_impl.push_fn(getter); // keys - let keys_type = RustType::Array(Box::new(key_type.clone())); + let keys_type = ConceptualRustType::Array(Box::new(key_type.clone())); let mut keys = codegen::Function::new("keys"); keys .arg_ref_self() @@ -2133,17 +2154,17 @@ fn key_encoding_field(name: &str, key: &FixedValue) -> EncodingField { } } -fn encoding_fields(name: &str, ty: &RustType) -> Vec { +fn encoding_fields(name: &str, ty: SerializingRustType) -> Vec { assert!(CLI_ARGS.preserve_encodings); - let x = match ty { - RustType::Array(elem_ty) => { + match ty { + SerializingRustType::Root(ConceptualRustType::Array(elem_ty)) => { let base = EncodingField { field_name: format!("{}_encoding", name), type_name: "LenEncoding".to_owned(), default_expr: "LenEncoding::default()", inner: Vec::new(), }; - let inner_encs = encoding_fields(&format!("{}_elem", name), elem_ty); + let inner_encs = encoding_fields(&format!("{}_elem", name), (&**elem_ty).into()); if inner_encs.is_empty() { vec![base] } else { @@ -2163,7 +2184,7 @@ fn encoding_fields(name: &str, ty: &RustType) -> Vec { ] } }, - RustType::Map(k, v) => { + SerializingRustType::Root(ConceptualRustType::Map(k, v)) => { let mut encs = vec![ EncodingField { field_name: format!("{}_encoding", name), @@ -2172,8 +2193,8 @@ fn encoding_fields(name: &str, ty: &RustType) -> Vec { inner: Vec::new(), } ]; - let key_encs = encoding_fields(&format!("{}_key", name), k); - let val_encs = encoding_fields(&format!("{}_value", name), v); + let key_encs = encoding_fields(&format!("{}_key", name), (&**k).into()); + let val_encs = encoding_fields(&format!("{}_value", name), (&**v).into()); if !key_encs.is_empty() { let type_name_value = if key_encs.len() == 1 { @@ -2204,7 +2225,7 @@ fn encoding_fields(name: &str, ty: &RustType) -> Vec { } encs }, - RustType::Primitive(p) => match p { + SerializingRustType::Root(ConceptualRustType::Primitive(p)) => match p { Primitive::Bytes | Primitive::Str => vec![ EncodingField { @@ -2232,28 +2253,27 @@ fn encoding_fields(name: &str, ty: &RustType) -> Vec { ], Primitive::Bool => /* bool only has 1 encoding */vec![], }, - RustType::Fixed(f) => match f { + SerializingRustType::Root(ConceptualRustType::Fixed(f)) => match f { FixedValue::Bool(_) | FixedValue::Null => vec![], - FixedValue::Nint(_) => encoding_fields(name, &RustType::Primitive(Primitive::I64)), - FixedValue::Uint(_) => encoding_fields(name, &RustType::Primitive(Primitive::U64)), - FixedValue::Text(_) => encoding_fields(name, &RustType::Primitive(Primitive::Str)), + FixedValue::Nint(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::I64)).into()), + FixedValue::Uint(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::U64)).into()), + FixedValue::Text(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::Str)).into()), }, - RustType::Alias(_, _) => panic!("resolve types before calling this"), - RustType::Tagged(tag, inner_ty) => { - let mut encs = encoding_fields(&format!("{}_tag", name), &RustType::Fixed(FixedValue::Uint(*tag))); - encs.append(&mut encoding_fields(name, &inner_ty)); + SerializingRustType::Root(ConceptualRustType::Alias(_, _)) => panic!("resolve types before calling this"), + SerializingRustType::Root(ConceptualRustType::Optional(ty)) => encoding_fields(name, (&**ty).into()), + SerializingRustType::Root(ConceptualRustType::Rust(_)) => vec![], + SerializingRustType::EncodingOperation(CBOREncodingOperation::Tagged(tag), child) => { + let mut encs = encoding_fields(&format!("{}_tag", name), (&ConceptualRustType::Fixed(FixedValue::Uint(*tag))).into()); + encs.append(&mut encoding_fields(name, *child)); encs - } - RustType::Optional(ty) => encoding_fields(name, ty), - RustType::Rust(_) => vec![], - RustType::CBORBytes(inner_ty) => { - let mut encs = encoding_fields(&format!("{}_bytes", name), &RustType::Primitive(Primitive::Bytes)); - encs.append(&mut encoding_fields(name, &inner_ty)); + }, + SerializingRustType::EncodingOperation(CBOREncodingOperation::CBORBytes, child) => { + let mut encs = encoding_fields(&format!("{}_bytes", name), (&ConceptualRustType::Primitive(Primitive::Bytes)).into()); + encs.append(&mut encoding_fields(name, *child)); encs - } - }; - x + }, + } } fn encoding_var_names_str(field_name: &str, rust_type: &RustType) -> String { @@ -2264,7 +2284,7 @@ fn encoding_var_names_str(field_name: &str, rust_type: &RustType) -> String { } else { vec![field_name.to_owned()] }; - for enc in encoding_fields(field_name, &resolved_rust_type).into_iter() { + for enc in encoding_fields(field_name, (&resolved_rust_type).into()).into_iter() { var_names.push(enc.field_name); } let var_names_str = if var_names.len() > 1 { @@ -2349,7 +2369,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na .vis("pub"); let mut native_new_block = Block::new("Self"); for field in &record.fields { - if !gen_scope.deserialize_generated_for_type(&field.rust_type) { + if !gen_scope.deserialize_generated_for_type(&field.rust_type.conceptual_type) { gen_scope.dont_generate_deserialize(name, format!("field {}: {} couldn't generate serialize", field.name, field.rust_type.for_rust_member(false))); } // Fixed values only exist in (de)serialization code (outside of preserve-encodings=true) @@ -2383,7 +2403,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } for field in &record.fields { // even fixed values still need to keep track of their encodings - for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases()) { + for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { encoding_struct.field(&format!("pub {}", field_enc.field_name), field_enc.type_name); } if record.rep == Representation::Map { @@ -2476,7 +2496,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na let mut optional_array_ser_block = Block::new(&format!("if let Some(field) = &self.{}", field.name)); gen_scope.generate_serialize( types, - &field.rust_type, + (&field.rust_type).into(), &mut optional_array_ser_block, SerializeConfig::new("field", &field.name).expr_is_ref(true).encoding_var_in_option_struct("self.encodings")); ser_func.push_block(optional_array_ser_block); @@ -2484,37 +2504,37 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { gen_scope.generate_serialize( types, - &field.rust_type, + (&field.rust_type).into(), &mut ser_func, SerializeConfig::new(format!("self.{}", field.name), &field.name).encoding_var_in_option_struct("self.encodings")); if CLI_ARGS.preserve_encodings { let var_names_str = encoding_var_names_str(&field.name, &field.rust_type); if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, &format!("let {} = ", var_names_str), "?;"); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "Ok(", ")", in_embedded, false, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "Ok(", ")", in_embedded, false, vec![], &mut err_deser, None); deser_body.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, &format!("let {} = ", var_names_str), ";", in_embedded, false, vec![], deser_body, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, &format!("let {} = ", var_names_str), ";", in_embedded, false, vec![], deser_body, None); } } else { if field.rust_type.is_fixed_value() { // don't set anything, only verify data if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, "", "?;"); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "", "", in_embedded, false, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "", "", in_embedded, false, vec![], &mut err_deser, None); // this block needs to evaluate to a Result even though it has no value err_deser.line("Ok(())"); deser_body.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "", "", in_embedded, false, vec![], deser_body, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "", "", in_embedded, false, vec![], deser_body, None); } } else { if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, &format!("let {} = ", field.name), "?;"); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "Ok(", ")", in_embedded, false, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "Ok(", ")", in_embedded, false, vec![], &mut err_deser, None); deser_body.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, &format!("let {} = ", field.name), ";", in_embedded, false, vec![], deser_body, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, &format!("let {} = ", field.name), ";", in_embedded, false, vec![], deser_body, None); } } } @@ -2532,7 +2552,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na for field in record.fields.iter() { // we don't support deserialization for optional fields so don't even bother if !field.optional { - for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases()) { + for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { encoding_ctor.line(format!("{},", field_enc.field_name)); } } @@ -2562,14 +2582,14 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na // {a, d, c, b}, {c, a, b, d}, etc which doesn't fit with the nature of deserialize_as_embedded_group // A possible solution would be to take all fields into one big map, either in generation to begin with, // or just for deserialization then constructing at the end with locals like a, b, bar_c, bar_d. - if let RustType::Rust(ident) = &field.rust_type { + if let ConceptualRustType::Rust(ident) = &field.rust_type.conceptual_type { if types.is_plain_group(&ident) { gen_scope.dont_generate_deserialize(name, format!("Map with plain group field {}: {}", field.name, field.rust_type.for_rust_member(false))); } } // declare variables for deser loop if CLI_ARGS.preserve_encodings { - for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases()) { + for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { deser_body.line(&format!("let mut {} = {};", field_enc.field_name, field_enc.default_expr)); } let key_enc = key_encoding_field(&field.name, &field.key.as_ref().unwrap()); @@ -2622,10 +2642,10 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na }; if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, &before, after); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "Ok(", ")", in_embedded, field.optional, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "Ok(", ")", in_embedded, field.optional, vec![], &mut err_deser, None); deser_block.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, &before, after, in_embedded, field.optional, vec![], deser_body, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, &before, after, in_embedded, field.optional, vec![], deser_body, None); } // Due to destructuring assignemnt (RFC 372 / 71156) being unstable we're forced to use temporaries then reassign after // which is not ideal but doing the assignment inside the lambda or otherwise has issues where it's putting lots of @@ -2639,7 +2659,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { deser_block.line(format!("{} = Some(tmp_{});", field.name, field.name)); } - for enc_field in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases()) { + for enc_field in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { deser_block.line(format!("{} = tmp_{};", enc_field.field_name, enc_field.field_name)); } } else { @@ -2650,11 +2670,11 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na // only does verification and sets the field_present bool to do error checking later if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, &format!("{}_present = ", field.name), "?;"); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "", "", in_embedded, field.optional, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "", "", in_embedded, field.optional, vec![], &mut err_deser, None); err_deser.line("Ok(true)"); deser_block.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "", "", in_embedded, field.optional, vec![], &mut deser_block, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "", "", in_embedded, field.optional, vec![], &mut deser_block, None); deser_block.line(&format!("{}_present = true;", field.name)); } } else { @@ -2663,10 +2683,10 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na deser_block.push_block(dup_check); if CLI_ARGS.annotate_fields { let mut err_deser = make_err_annotate_block(&field.name, &format!("{} = Some(", field.name), "?);"); - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, "Ok(", ")", in_embedded, field.optional, vec![], &mut err_deser, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, "Ok(", ")", in_embedded, field.optional, vec![], &mut err_deser, None); deser_block.push_block(err_deser); } else { - gen_scope.generate_deserialize(types, &field.rust_type, &field.name, &format!("{} = Some(", field.name), ");", in_embedded, field.optional, vec![], &mut deser_block, None); + gen_scope.generate_deserialize(types, (&field.rust_type).into(), &field.name, &format!("{} = Some(", field.name), ");", in_embedded, field.optional, vec![], &mut deser_block, None); } } } @@ -2705,7 +2725,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na // serialize value gen_scope.generate_serialize( types, - &field.rust_type, + (&field.rust_type).into(), &mut map_ser_content, serialize_config); ser_content.push((field_index, field, map_ser_content)); @@ -2855,7 +2875,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na for field in record.fields.iter() { let key_enc = key_encoding_field(&field.name, field.key.as_ref().unwrap()); encoding_ctor.line(format!("{},", key_enc.field_name)); - for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases()) { + for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { encoding_ctor.line(format!("{},", field_enc.field_name)); } } @@ -2919,14 +2939,14 @@ fn codegen_group_choices(gen_scope: &mut GenerationScope, types: &IntermediateTy let mut output_comma = false; // We only want to generate Variant::new() calls when we created a special struct // for the variant, which happens in the general case for multi-field group choices - let fields = match &variant.rust_type { + let fields = match &variant.rust_type.conceptual_type { // we need to check for sanity here, as if we're referring to the ident // it should at this stage be registered - RustType::Rust(ident) => match types.rust_struct(ident).unwrap().variant() { + ConceptualRustType::Rust(ident) => match types.rust_struct(ident).unwrap().variant() { RustStructType::Record(record) => Some(&record.fields), _ => None, }, - RustType::Alias(_, _) => unimplemented!("TODO: do we need to handle aliases here?"), + ConceptualRustType::Alias(_, _) => unimplemented!("TODO: do we need to handle aliases here?"), _ => None, }; match fields { @@ -3058,7 +3078,7 @@ impl EnumVariantInRust { fn new(variant: &EnumVariant, rep: Option) -> Self { let name = variant.name_as_var(); let mut enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(&name, &variant.rust_type.clone().resolve_aliases()) + encoding_fields(&name, (&variant.rust_type.clone().resolve_aliases()).into()) } else { vec![] }; @@ -3251,10 +3271,10 @@ fn generate_enum(gen_scope: &mut GenerationScope, types: &IntermediateTypes, nam let mut output_comma = false; // We only want to generate Variant::new() calls when we created a special struct // for the variant, which happens in the general case for multi-field group choices - let fields = match variant.rust_type.strip_to_semantical_type() { + let fields = match &variant.rust_type.conceptual_type { // we need to check for sanity here, as if we're referring to the ident // it should at this stage be registered - RustType::Rust(ident) => { + ConceptualRustType::Rust(ident) => { match types.rust_struct(ident).expect(&format!("{} refers to undefined ident: {}", name, ident)).variant() { RustStructType::Record(record) => Some(&record.fields), @@ -3320,7 +3340,7 @@ fn generate_enum(gen_scope: &mut GenerationScope, types: &IntermediateTypes, nam start_len(&mut case_block, r, "serializer", "outer_len_encoding", &n.to_string()); gen_scope.generate_serialize( types, - &variant.rust_type, + (&variant.rust_type).into(), &mut case_block, SerializeConfig::new(&variant_var_name, &variant_var_name) .expr_is_ref(true) @@ -3331,7 +3351,7 @@ fn generate_enum(gen_scope: &mut GenerationScope, types: &IntermediateTypes, nam // type choice gen_scope.generate_serialize( types, - &variant.rust_type, + (&variant.rust_type).into(), &mut case_block, SerializeConfig::new(&variant_var_name, &variant_var_name) .expr_is_ref(true) @@ -3359,7 +3379,7 @@ fn generate_enum(gen_scope: &mut GenerationScope, types: &IntermediateTypes, nam // but we'd just want to inline the single one inside of a line... gen_scope.generate_serialize( types, - &variant.rust_type, + (&variant.rust_type).into(), &mut case_block, SerializeConfig::new(&variant_var_name, &variant_var_name) .expr_is_ref(true) @@ -3376,10 +3396,10 @@ fn generate_enum(gen_scope: &mut GenerationScope, types: &IntermediateTypes, nam // TODO: how to detect when a greedy match won't work? (ie choice with choices in a choice possibly) let mut variant_deser = Block::new("match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError>"); if enum_gen_info.types.is_empty() { - gen_scope.generate_deserialize(types, &variant.rust_type, &variant.name_as_var(), "", "", false, false, vec![], &mut variant_deser, None); + gen_scope.generate_deserialize(types, (&variant.rust_type).into(), &variant.name_as_var(), "", "", false, false, vec![], &mut variant_deser, None); variant_deser.line("Ok(())"); } else { - gen_scope.generate_deserialize(types, &variant.rust_type, &variant.name_as_var(), "Ok(", ")", false, false, vec![], &mut variant_deser, None); + gen_scope.generate_deserialize(types, (&variant.rust_type).into(), &variant.name_as_var(), "Ok(", ")", false, false, vec![], &mut variant_deser, None); } variant_deser.after(")(raw)"); deser_body.push_block(variant_deser); @@ -3527,7 +3547,7 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate let encoding_name = RustIdent::new(CDDLIdent::new(format!("{}Encoding", type_name))); let enc_fields = if CLI_ARGS.preserve_encodings { s.field("pub inner", field_type.for_rust_member(false)); - let enc_fields = encoding_fields("inner", &field_type.clone().resolve_aliases()); + let enc_fields = encoding_fields("inner", (&field_type.clone().resolve_aliases()).into()); if !enc_fields.is_empty() { s.field(&format!("{}pub encodings", encoding_var_macros(types.used_as_key(type_name))), format!("Option<{}>", encoding_name)); @@ -3569,14 +3589,14 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate let mut ser_impl = make_serialization_impl(&type_name.to_string()); gen_scope.generate_serialize( types, - &field_type, + field_type.into(), &mut ser_func, SerializeConfig::new(self_var, "inner").is_end(true).encoding_var_in_option_struct("self.encodings")); ser_impl.push_fn(ser_func); let mut deser_func = make_deserialization_function("deserialize"); let mut deser_impl = codegen::Impl::new(&type_name.to_string()); deser_impl.impl_trait("Deserialize"); - if let RustType::Rust(id) = field_type { + if let ConceptualRustType::Rust(id) = &field_type.conceptual_type { if types.is_plain_group(id) { unimplemented!("TODO: make len/read_len variables of appropriate sizes so the generated code compiles"); } @@ -3596,31 +3616,35 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate } else { (format!("let {} = ", var_names_str), ";") }; - gen_scope.generate_deserialize(types, field_type, "inner", &before, after, false, false, vec![], &mut deser_func, None); + gen_scope.generate_deserialize(types, field_type.into(), "inner", &before, after, false, false, vec![], &mut deser_func, None); - let against = match field_type.strip_to_serialization_type().strip_tag() { - RustType::Primitive(p) => match p { - Primitive::Bytes | - Primitive::Str => "inner.len()", - Primitive::Bool | - Primitive::U8 | - Primitive::U16 | - Primitive::U32 | - Primitive::U64 | - Primitive::I8 | - Primitive::I16 | - Primitive::I32 | - Primitive::I64 | - Primitive::N64 => "inner", - }, - _ => unimplemented!(), + let against = if field_type.encodings.contains(&CBOREncodingOperation::CBORBytes) { + "inner.len()" + } else { + match &field_type.conceptual_type { + ConceptualRustType::Primitive(p) => match p { + Primitive::Bytes | + Primitive::Str => "inner.len()", + Primitive::Bool | + Primitive::U8 | + Primitive::U16 | + Primitive::U32 | + Primitive::U64 | + Primitive::I8 | + Primitive::I16 | + Primitive::I32 | + Primitive::I64 | + Primitive::N64 => "inner", + }, + _ => unimplemented!(), + } }; let mut check = match (min, max) { (Some(min), Some(max)) => if min == max { Block::new(&format!("if {} != {}", against, min)) } else { - let non_negative = match field_type.strip_to_serialization_type().strip_tag() { - RustType::Primitive(p) => match p { + let non_negative = field_type.encodings.is_empty() && match &field_type.conceptual_type { + ConceptualRustType::Primitive(p) => match p { Primitive::Bytes | Primitive::Str => true, Primitive::Bool | @@ -3698,7 +3722,7 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate try_from } else { // let field_type_tagged = if let Some(t) = tag { - // RustType::Tagged(t, Box::new(field_type.clone())) + // ConceptualRustType::Tagged(t, Box::new(field_type.clone())) // } else { // field_type.clone() // }; @@ -3710,7 +3734,7 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate } else { (format!("let {} = ", var_names_str), ";") }; - gen_scope.generate_deserialize(types, &field_type, "inner", &before, after, false, false, vec![], &mut deser_func, None); + gen_scope.generate_deserialize(types, field_type.into(), "inner", &before, after, false, false, vec![], &mut deser_func, None); let mut deser_ctor = codegen::Block::new("Ok(Self"); deser_ctor.line("inner,"); @@ -3732,7 +3756,7 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate } new_func.push_block(ctor_block); } else { - gen_scope.generate_deserialize(types, &field_type, "inner", "Ok(Self(", "))", false, false, vec![], &mut deser_func, None); + gen_scope.generate_deserialize(types, field_type.into(), "inner", "Ok(Self(", "))", false, false, vec![], &mut deser_func, None); new_func.line("Self(inner)"); } diff --git a/src/intermediate.rs b/src/intermediate.rs index 3ee5628..631e599 100644 --- a/src/intermediate.rs +++ b/src/intermediate.rs @@ -66,23 +66,23 @@ impl<'a> IntermediateTypes<'a> { let ident = AliasIdent::new(CDDLIdent::new(name)); aliases.insert(ident.clone(), (rust_type, false, false)); }; - insert_alias("uint", RustType::Primitive(Primitive::U64)); - insert_alias("nint", RustType::Primitive(Primitive::N64)); - insert_alias("bool", RustType::Primitive(Primitive::Bool)); + insert_alias("uint", ConceptualRustType::Primitive(Primitive::U64).into()); + insert_alias("nint", ConceptualRustType::Primitive(Primitive::N64).into()); + insert_alias("bool", ConceptualRustType::Primitive(Primitive::Bool).into()); // TODO: define enum or something as otherwise it can overflow i64 // and also we can't define the serialization traits for types // that are defined outside of this crate (includes primitives) //"int" => "i64", - let string_type = RustType::Primitive(Primitive::Str); + let string_type: RustType = ConceptualRustType::Primitive(Primitive::Str).into(); insert_alias("tstr", string_type.clone()); insert_alias("text", string_type); - insert_alias("bstr", RustType::Primitive(Primitive::Bytes)); - insert_alias("bytes", RustType::Primitive(Primitive::Bytes)); - let null_type = RustType::Fixed(FixedValue::Null); + insert_alias("bstr", ConceptualRustType::Primitive(Primitive::Bytes).into()); + insert_alias("bytes", ConceptualRustType::Primitive(Primitive::Bytes).into()); + let null_type: RustType = ConceptualRustType::Fixed(FixedValue::Null).into(); insert_alias("null", null_type.clone()); insert_alias("nil", null_type); - insert_alias("true", RustType::Fixed(FixedValue::Bool(true))); - insert_alias("false", RustType::Fixed(FixedValue::Bool(false))); + insert_alias("true", ConceptualRustType::Fixed(FixedValue::Bool(true)).into()); + insert_alias("false", ConceptualRustType::Fixed(FixedValue::Bool(false)).into()); // What about bingint/other stuff in the standard prelude? aliases } @@ -95,12 +95,12 @@ impl<'a> IntermediateTypes<'a> { let resolved = match self.apply_type_aliases(&alias_ident) { Some(ty) => match alias_ident { AliasIdent::Reserved(_) => ty, - AliasIdent::Rust(_) => RustType::Alias(alias_ident.clone(), Box::new(ty)) + AliasIdent::Rust(_) => ty.as_alias(alias_ident.clone()) }, - None => RustType::Rust(RustIdent::new(raw.clone())), + None => ConceptualRustType::Rust(RustIdent::new(raw.clone())).into(), }; - let resolved_inner = match &resolved { - RustType::Alias(_, ty) => ty, + let resolved_inner = match &resolved.conceptual_type { + ConceptualRustType::Alias(_, ty) => ty, ty => ty, }; if CLI_ARGS.binary_wrappers { @@ -108,8 +108,8 @@ impl<'a> IntermediateTypes<'a> { // we would have generated a named wrapper object so we should // refer to that instead if !is_identifier_reserved(&raw.to_string()) { - if let RustType::Primitive(Primitive::Bytes) = resolved_inner { - return RustType::Rust(RustIdent::new(raw.clone())); + if let ConceptualRustType::Primitive(Primitive::Bytes) = resolved_inner { + return ConceptualRustType::Rust(RustIdent::new(raw.clone())).into(); } } } @@ -117,10 +117,10 @@ impl<'a> IntermediateTypes<'a> { // specifically handle this case since you wouldn't know whether you hit a break // or are reading a key here, unless we check, but then you'd need to store the // non-break special value once read - if let RustType::Array(ty) = resolved_inner { + if let ConceptualRustType::Array(ty) = resolved_inner { assert!(!ty.cbor_types().contains(&CBORType::Special)); } - if let RustType::Map(key_type, _val_type) = resolved_inner { + if let ConceptualRustType::Map(key_type, _val_type) = resolved_inner { assert!(!key_type.cbor_types().contains(&CBORType::Special)); } resolved @@ -140,14 +140,14 @@ impl<'a> IntermediateTypes<'a> { // we auto-include only the parts of the cddl prelude necessary (and supported) cddl_prelude(reserved).expect(&format!("Reserved ident {} not a part of cddl_prelude?", reserved)); self.emit_prelude(reserved.clone()); - Some(RustType::Rust(RustIdent::new(CDDLIdent::new(format!("prelude_{}", reserved))))) + Some(ConceptualRustType::Rust(RustIdent::new(CDDLIdent::new(format!("prelude_{}", reserved)))).into()) }, }, } } pub fn register_type_alias(&mut self, alias: RustIdent, base_type: RustType, generate_rust_alias: bool, generate_wasm_alias: bool) { - if let RustType::Alias(_ident, _ty) = &base_type { + if let ConceptualRustType::Alias(_ident, _ty) = &base_type.conceptual_type { panic!("register_type_alias*({}, {:?}) wrap automatically in Alias, no need to provide it.", alias, base_type); } self.type_aliases.insert(alias.into(), (base_type, generate_rust_alias, generate_wasm_alias)); @@ -163,28 +163,28 @@ impl<'a> IntermediateTypes<'a> { RustStructType::Table { domain, range } => { // we must provide the keys type to return if CLI_ARGS.wasm { - self.create_and_register_array_type(domain.clone(), &domain.name_as_wasm_array()); + self.create_and_register_array_type(domain.clone(), &domain.conceptual_type.name_as_wasm_array()); } - if rust_struct.tag.is_none() { - self.register_type_alias( - rust_struct.ident.clone(), - RustType::Map(Box::new(domain.clone()), Box::new(range.clone())), - true, - false) - } else { - unimplemented!("what to do here?"); + let mut map_type: RustType = ConceptualRustType::Map(Box::new(domain.clone()), Box::new(range.clone())).into(); + if let Some(tag) = rust_struct.tag { + map_type = map_type.tag(tag); } + self.register_type_alias( + rust_struct.ident.clone(), + map_type, + true, + false) }, RustStructType::Array { element_type } => { - if rust_struct.tag.is_none() { - self.register_type_alias( - rust_struct.ident.clone(), - RustType::Array(Box::new(element_type.clone())), - true, - false) - } else { - unimplemented!("what to do here?"); + let mut array_type: RustType = ConceptualRustType::Array(Box::new(element_type.clone())).into(); + if let Some(tag) = rust_struct.tag { + array_type = array_type.tag(tag); } + self.register_type_alias( + rust_struct.ident.clone(), + array_type, + true, + false) }, RustStructType::Wrapper { min_max: Some(_) , ..} => { self.mark_new_can_fail(rust_struct.ident.clone()); @@ -196,23 +196,23 @@ impl<'a> IntermediateTypes<'a> { // creates a RustType for the array type - and if needed, registers a type to generate // TODO: After the split we should be able to only register it directly - // and then examine those at generation-time and handle things ALWAYS as RustType::Array + // and then examine those at generation-time and handle things ALWAYS as ConceptualRustType::Array pub fn create_and_register_array_type(&mut self, element_type: RustType, array_type_name: &str) -> RustType { - let raw_arr_type = RustType::Array(Box::new(element_type.clone())); + let raw_arr_type = ConceptualRustType::Array(Box::new(element_type.clone())); // only generate an array wrapper if we can't wasm-expose it raw if raw_arr_type.directly_wasm_exposable() { - return raw_arr_type; + return raw_arr_type.into(); } let array_type_ident = RustIdent::new(CDDLIdent::new(array_type_name)); // If we are the only thing referring to our element and it's a plain group // we must mark it as being serialized as an array - if let RustType::Rust(_) = &element_type { + if let ConceptualRustType::Rust(_) = &element_type.conceptual_type { self.set_rep_if_plain_group(&array_type_ident, Representation::Array); } // we don't pass in tags here. If a tag-wrapped array is done I think it generates // 2 separate types (array wrapper -> tag wrapper struct) self.register_rust_struct(RustStruct::new_array(array_type_ident, None, element_type.clone())); - RustType::Array(Box::new(element_type)) + ConceptualRustType::Array(Box::new(element_type)).into() } pub fn register_generic_def(&mut self, def: GenericDef) { @@ -236,13 +236,13 @@ impl<'a> IntermediateTypes<'a> { // recursively check all types used as keys or contained within a type used as a key // this is so we only derive comparison or hash traits for those types let mut used_as_key = BTreeSet::new(); - fn mark_used_as_key(ty: &RustType, used_as_key: &mut BTreeSet) { - if let RustType::Rust(ident) = ty { + fn mark_used_as_key(ty: &ConceptualRustType, used_as_key: &mut BTreeSet) { + if let ConceptualRustType::Rust(ident) = ty { used_as_key.insert(ident.clone()); } } - fn check_used_as_key<'a>(ty: &RustType, types: &IntermediateTypes<'a>, used_as_key: &mut BTreeSet) { - if let RustType::Map(k, _v) = ty { + fn check_used_as_key<'a>(ty: &ConceptualRustType, types: &IntermediateTypes<'a>, used_as_key: &mut BTreeSet) { + if let ConceptualRustType::Map(k, _v) = ty { k.visit_types(types, &mut |ty| mark_used_as_key(ty, used_as_key)); } } @@ -255,7 +255,7 @@ impl<'a> IntermediateTypes<'a> { self.used_as_key = used_as_key; } - pub fn visit_types(&self, f: &mut F) { + pub fn visit_types(&self, f: &mut F) { for rust_struct in self.rust_structs().values() { rust_struct.visit_types(self, f); } @@ -264,7 +264,7 @@ impl<'a> IntermediateTypes<'a> { pub fn is_referenced(&self, ident: &RustIdent) -> bool { let mut found = false; self.visit_types(&mut |ty| match ty { - RustType::Rust(id) => if id == ident { + ConceptualRustType::Rust(id) => if id == ident { found = true }, _ => (), @@ -640,8 +640,134 @@ mod idents { } pub use idents::*; +/// Details on how to encode a rust type in CBOR. Order is important +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CBOREncodingOperation { + /// CBOR tagged type + Tagged(usize), + /// bytes .cbor T in cddl, outside of serialization is semantically like T + CBORBytes, +} + +/// A complete rust type, including serialization options that don't impact other areas +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RustType { + /// Conceptual type i.e. how it's used in non-serialization contexts + pub conceptual_type: ConceptualRustType, + /// How to encode the conceptual type. Order is important. Applied in iteration order. + pub encodings: Vec, + // default value when missing in deserialization + pub default: Option, +} + +impl std::ops::Deref for RustType { + type Target = ConceptualRustType; + + fn deref(&self) -> &Self::Target { + &self.conceptual_type + } +} + +impl RustType { + pub fn new(conceptual_type: ConceptualRustType) -> Self { + Self { + conceptual_type, + encodings: Vec::new(), + default: None, + } + } + + pub fn as_alias(mut self, alias_ident: AliasIdent) -> Self { + self.conceptual_type = ConceptualRustType::Alias(alias_ident, Box::new(self.conceptual_type)); + self + } + + pub fn tag(mut self, tag: usize) -> Self { + self.encodings.push(CBOREncodingOperation::Tagged(tag)); + self + } + + pub fn default(mut self, default_value: FixedValue) -> Self { + assert!(self.default.is_none()); + // TODO: verify that the fixed value makes sense for the conceptual_type + self.default = Some(default_value); + self + } + + pub fn as_bytes(mut self) -> Self { + self.encodings.push(CBOREncodingOperation::CBORBytes); + self + } + + pub fn resolve_aliases(self) -> Self { + Self { + conceptual_type: self.conceptual_type.resolve_aliases(), + encodings: self.encodings, + default: self.default, + } + } + + pub fn cbor_types(&self) -> Vec { + match self.encodings.last() { + Some(CBOREncodingOperation::Tagged(_)) => vec![CBORType::Tag], + Some(CBOREncodingOperation::CBORBytes) => vec![CBORType::Bytes], + None => match &self.conceptual_type { + ConceptualRustType::Fixed(f) => vec![match f { + FixedValue::Uint(_) => CBORType::UnsignedInteger, + FixedValue::Nint(_) => CBORType::NegativeInteger, + FixedValue::Text(_) => CBORType::Text, + FixedValue::Null => CBORType::Special, + FixedValue::Bool(_) => CBORType::Special, + }], + ConceptualRustType::Primitive(p) => p.cbor_types(), + ConceptualRustType::Rust(_ident) => { + //panic!("TODO: store first cbor tag somewhere") + vec![CBORType::Array, CBORType::Map] + }, + ConceptualRustType::Array(_) => vec![CBORType::Array], + ConceptualRustType::Map(_k, _v) => vec![CBORType::Map], + ConceptualRustType::Optional(ty) => { + let mut inner_types = ty.cbor_types(); + if !inner_types.contains(&CBORType::Special) { + inner_types.push(CBORType::Special); + } + inner_types + }, + ConceptualRustType::Alias(_ident, ty) => Self::new((**ty).clone()).cbor_types(), + } + } + } + + fn _cbor_special_type(&self) -> Option { + unimplemented!() + } + + fn _is_serialize_multiline(&self) -> bool { + if self.encodings.is_empty() { + match &self.conceptual_type { + ConceptualRustType::Fixed(_) => false, + ConceptualRustType::Primitive(_) => false, + ConceptualRustType::Rust(_) => false, + ConceptualRustType::Array(_) => true, + ConceptualRustType::Optional(_) => false, + ConceptualRustType::Map(_, _) => false, + ConceptualRustType::Alias(_ident, ty) => Self::new((**ty).clone())._is_serialize_multiline(), + } + } else { + true + } + } +} + +impl std::convert::From for RustType { + fn from(conceptual_type: ConceptualRustType) -> Self { + Self::new(conceptual_type) + } +} + +/// How a type will be represented in rust outside of a serialization context #[derive(Clone, Debug, Eq, PartialEq)] -pub enum RustType { +pub enum ConceptualRustType { Fixed(FixedValue), // Primitive type that can be passed to/from wasm Primitive(Primitive), @@ -649,16 +775,12 @@ pub enum RustType { Rust(RustIdent), // Array-wrapped type. Passed as Vec if T is Primitive Array(Box), - // Tagged type. Behavior depends entirely on wrapped type. - Tagged(usize, Box), // T / null in CDDL - auto-converts to Option in rust for ease of use. Optional(Box), // TODO: table type to support inlined defined table-type groups as fields Map(Box, Box), // Alias for another type - Alias(AliasIdent, Box), - // bytes .cbor T in cddl, outside of serialization is semantically like T - CBORBytes(Box), + Alias(AliasIdent, Box), // TODO: for non-table-type ones we could define a RustField(Ident, RustType) and then // a variant here Struct(Vec) and delegate field/argument generation to @@ -667,34 +789,31 @@ pub enum RustType { // another approach would be necessary. } -impl RustType { +impl ConceptualRustType { pub fn resolve_aliases(self) -> Self { match self { - RustType::Array(ty) => RustType::Array(Box::new(ty.resolve_aliases())), - RustType::Tagged(tag, ty) => RustType::Tagged(tag, Box::new(ty.resolve_aliases())), - RustType::Alias(_, ty) => ty.resolve_aliases(), - RustType::Map(key, value) => RustType::Map(Box::new(key.resolve_aliases()), Box::new(value.resolve_aliases())), - RustType::Optional(ty) => RustType::Optional(Box::new(ty.resolve_aliases())), - RustType::CBORBytes(ty) => RustType::CBORBytes(Box::new(ty.resolve_aliases())), + Self::Array(ty) => Self::Array(Box::new(ty.resolve_aliases())), + Self::Alias(_, ty) => ty.resolve_aliases(), + Self::Map(key, value) => Self::Map(Box::new(key.resolve_aliases()), Box::new(value.resolve_aliases())), + Self::Optional(ty) => Self::Optional(Box::new(ty.resolve_aliases())), _ => self, } } pub fn directly_wasm_exposable(&self) -> bool { match self { - RustType::Fixed(_) => false, - RustType::Primitive(_) => true, - RustType::Rust(_) => false, + Self::Fixed(_) => false, + Self::Primitive(_) => true, + Self::Rust(_) => false, // wasm_bindgen doesn't support nested vecs, even if the inner vec would be supported - RustType::Array(ty) => { - let inner = match &**ty { - RustType::Alias(_ident, ty) => ty, - RustType::Optional(ty) => ty, - RustType::Tagged(_tag, ty) => ty, + Self::Array(ty) => { + let inner = match &ty.conceptual_type { + Self::Alias(_ident, ty) => &*ty, + Self::Optional(ty) => &ty.conceptual_type, ty => ty, }; match inner { - RustType::Primitive(p) => match p { + Self::Primitive(p) => match p { // converts to js number which is supported as Vec Primitive::Bool | Primitive::I8 | @@ -711,30 +830,26 @@ impl RustType { // Vec is not supported by wasm-bindgen Primitive::Str => false, }, - RustType::Array(_) => false, - _ => ty.directly_wasm_exposable(), + Self::Array(_) => false, + _ => ty.conceptual_type.directly_wasm_exposable(), } }, - RustType::Tagged(_tag, ty) => ty.directly_wasm_exposable(), - RustType::Optional(ty) => ty.directly_wasm_exposable(), - RustType::Map(_, _) => false, - RustType::Alias(_ident, ty) => ty.directly_wasm_exposable(), - RustType::CBORBytes(ty) => ty.directly_wasm_exposable(), + Self::Optional(ty) => ty.conceptual_type.directly_wasm_exposable(), + Self::Map(_, _) => false, + Self::Alias(_ident, ty) => ty.directly_wasm_exposable(), } } pub fn is_fixed_value(&self) -> bool { match self { - RustType::Fixed(_) => true, - RustType::Tagged(_tag, ty) => ty.is_fixed_value(), - RustType::Alias(_ident, ty) => ty.is_fixed_value(), - RustType::CBORBytes(ty) => ty.is_fixed_value(), + Self::Fixed(_) => true, + Self::Alias(_ident, ty) => ty.is_fixed_value(), _ => false, } } pub fn name_as_wasm_array(&self) -> String { - if RustType::Array(Box::new(self.clone())).directly_wasm_exposable() { + if Self::Array(Box::new(self.clone().into())).directly_wasm_exposable() { format!("Vec<{}>", self.for_wasm_member()) } else { format!("{}s", self.for_variant()) @@ -753,20 +868,18 @@ impl RustType { /// Function parameter TYPE by-non-mut-reference for read-only pub fn for_rust_read(&self) -> String { match self { - RustType::Fixed(_) => panic!("should not expose Fixed type, only here for serialization: {:?}", self), - RustType::Primitive(p) => p.to_string(), - RustType::Rust(ident) => format!("&{}", ident), - RustType::Array(ty) => format!("&{}", ty.name_as_rust_array(false)), - RustType::Tagged(_tag, ty) => ty.for_rust_read(), - RustType::Optional(ty) => format!("Option<{}>", ty.for_rust_read()), - RustType::Map(_k, _v) => format!("&{}", self.for_rust_member(false)), - RustType::Alias(ident, ty) => match &**ty { + Self::Fixed(_) => panic!("should not expose Fixed type, only here for serialization: {:?}", self), + Self::Primitive(p) => p.to_string(), + Self::Rust(ident) => format!("&{}", ident), + Self::Array(ty) => format!("&{}", ty.conceptual_type.name_as_rust_array(false)), + Self::Optional(ty) => format!("Option<{}>", ty.conceptual_type.for_rust_read()), + Self::Map(_k, _v) => format!("&{}", self.for_rust_member(false)), + Self::Alias(ident, ty) => match &**ty { // TODO: ??? - RustType::Rust(_) => format!("&{}", ident), + Self::Rust(_) => format!("&{}", ident), _ => ident.to_string(), }, - RustType::CBORBytes(ty) => ty.for_rust_read(), } } @@ -782,24 +895,22 @@ impl RustType { "&" }; match self { - RustType::Fixed(_) => panic!("should not expose Fixed type to wasm, only here for serialization: {:?}", self), - RustType::Primitive(p) => p.to_string(), - RustType::Rust(ident) => format!("{}{}", opt_ref, ident), - RustType::Array(ty) => if self.directly_wasm_exposable() { - ty.name_as_wasm_array() + Self::Fixed(_) => panic!("should not expose Fixed type to wasm, only here for serialization: {:?}", self), + Self::Primitive(p) => p.to_string(), + Self::Rust(ident) => format!("{}{}", opt_ref, ident), + Self::Array(ty) => if self.directly_wasm_exposable() { + ty.conceptual_type.name_as_wasm_array() } else { - format!("{}{}", opt_ref, ty.name_as_wasm_array()) + format!("{}{}", opt_ref, ty.conceptual_type.name_as_wasm_array()) }, - RustType::Tagged(_tag, ty) => ty.for_wasm_param_impl(force_not_ref), - RustType::Optional(ty) => format!("Option<{}>", ty.for_wasm_param_impl(true)), - RustType::Map(_k, _v) => format!("{}{}", opt_ref, self.for_wasm_member()), - // it might not be worth generating this as alises are ignored by wasm-pack build, but + Self::Optional(ty) => format!("Option<{}>", ty.conceptual_type.for_wasm_param_impl(true)), + Self::Map(_k, _v) => format!("{}{}", opt_ref, self.for_wasm_member()), + // it might not be worth generating this as aliases are ignored by wasm-pack build, but // that could change in the future so as long as it doens't cause issues we'll leave it - RustType::Alias(ident, ty) => match &**ty { - RustType::Rust(_) => format!("{}{}", opt_ref, ident), + Self::Alias(ident, ty) => match &**ty { + Self::Rust(_) => format!("{}{}", opt_ref, ident), _ => ident.to_string(), } - RustType::CBORBytes(ty) => ty.for_wasm_param(), } } @@ -809,31 +920,29 @@ impl RustType { } pub fn name_for_wasm_map(k: &RustType, v: &RustType) -> RustIdent { - RustIdent::new(CDDLIdent::new(format!("Map{}To{}", k.for_variant(), v.for_variant()))) + RustIdent::new(CDDLIdent::new(format!("Map{}To{}", k.conceptual_type.for_variant(), v.conceptual_type.for_variant()))) } pub fn name_for_rust_map(k: &RustType, v: &RustType, from_wasm: bool) -> String { - format!("{}<{}, {}>", table_type(), k.for_rust_member(from_wasm), v.for_rust_member(from_wasm)) + format!("{}<{}, {}>", table_type(), k.conceptual_type.for_rust_member(from_wasm), v.conceptual_type.for_rust_member(from_wasm)) } /// If we were to store a value directly in a wasm-wrapper, this would be used. pub fn for_wasm_member(&self) -> String { match self { - RustType::Fixed(_) => panic!("should not expose Fixed type in member, only needed for serializaiton: {:?}", self), - RustType::Primitive(p) => p.to_string(), - RustType::Rust(ident) => ident.to_string(), - RustType::Array(ty) => ty.name_as_wasm_array(), - RustType::Tagged(_tag, ty) => ty.for_wasm_member(), - RustType::Optional(ty) => format!("Option<{}>", ty.for_wasm_member()), - RustType::Map(k, v) => Self::name_for_wasm_map(k, v).to_string(), - RustType::Alias(ident, ty) => match ident { + Self::Fixed(_) => panic!("should not expose Fixed type in member, only needed for serializaiton: {:?}", self), + Self::Primitive(p) => p.to_string(), + Self::Rust(ident) => ident.to_string(), + Self::Array(ty) => ty.conceptual_type.name_as_wasm_array(), + Self::Optional(ty) => format!("Option<{}>", ty.conceptual_type.for_wasm_member()), + Self::Map(k, v) => Self::name_for_wasm_map(k, v).to_string(), + Self::Alias(ident, ty) => match ident { // we don't generate type aliases for reserved types, just transform // them into rust equivalents, so we can't and shouldn't use their alias here. AliasIdent::Reserved(_) => ty.for_wasm_member(), // but other aliases are generated and should be used. AliasIdent::Rust(_) => ident.to_string(), }, - RustType::CBORBytes(ty) => ty.for_wasm_member(), } } @@ -845,40 +954,36 @@ impl RustType { "" }; match self { - RustType::Fixed(_) => panic!("should not expose Fixed type in member, only needed for serializaiton: {:?}", self), - RustType::Primitive(p) => p.to_string(), - RustType::Rust(ident) => format!("{}{}", core, ident), - RustType::Array(ty) => ty.name_as_rust_array(from_wasm), - RustType::Tagged(_tag, ty) => ty.for_rust_member(from_wasm), - RustType::Optional(ty) => format!("Option<{}>", ty.for_rust_member(from_wasm)), - RustType::Map(k, v) => Self::name_for_rust_map(k, v, from_wasm), - RustType::Alias(ident, ty) => match ident { + Self::Fixed(_) => panic!("should not expose Fixed type in member, only needed for serializaiton: {:?}", self), + Self::Primitive(p) => p.to_string(), + Self::Rust(ident) => format!("{}{}", core, ident), + Self::Array(ty) => ty.conceptual_type.name_as_rust_array(from_wasm), + Self::Optional(ty) => format!("Option<{}>", ty.conceptual_type.for_rust_member(from_wasm)), + Self::Map(k, v) => Self::name_for_rust_map(k, v, from_wasm), + Self::Alias(ident, ty) => match ident { // we don't generate type aliases for reserved types, just transform // them into rust equivalents, so we can't and shouldn't use their alias here. AliasIdent::Reserved(_) => ty.for_rust_member(from_wasm), // but other aliases are generated and should be used. AliasIdent::Rust(_) => format!("{}{}", core, ident), }, - RustType::CBORBytes(ty) => ty.for_rust_member(from_wasm), } } /// IDENTIFIER for an enum variant. (Use for_rust_member() for the ) pub fn for_variant(&self) -> VariantIdent { match self { - RustType::Fixed(f) => f.for_variant(), - RustType::Primitive(p) => p.to_variant(), - RustType::Rust(ident) => VariantIdent::new_rust(ident.clone()), - RustType::Array(inner) => VariantIdent::new_custom(format!("Arr{}", inner.for_variant())), - RustType::Tagged(_tag, ty) => ty.for_variant(), + Self::Fixed(f) => f.for_variant(), + Self::Primitive(p) => p.to_variant(), + Self::Rust(ident) => VariantIdent::new_rust(ident.clone()), + Self::Array(inner) => VariantIdent::new_custom(format!("Arr{}", inner.conceptual_type.for_variant())), // TODO: should we not end up in this situation and just insert a Null fixed value instead? - RustType::Optional(ty) => VariantIdent::new_custom(format!("Opt{}", ty.for_variant())), - RustType::Map(k, v) => VariantIdent::new_custom(Self::name_for_wasm_map(k, v).to_string()), - RustType::Alias(ident, _ty) => match ident { + Self::Optional(ty) => VariantIdent::new_custom(format!("Opt{}", ty.conceptual_type.for_variant())), + Self::Map(k, v) => VariantIdent::new_custom(Self::name_for_wasm_map(k, v).to_string()), + Self::Alias(ident, _ty) => match ident { AliasIdent::Rust(rust_ident) => VariantIdent::new_rust(rust_ident.clone()), AliasIdent::Reserved(reserved) => VariantIdent::new_custom(reserved), }, - RustType::CBORBytes(ty) => VariantIdent::new_custom(format!("CBORBytes{}", ty.for_variant())), } } @@ -886,57 +991,56 @@ impl RustType { /// can_fail is for cases where checks (e.g. range checks) are done if there /// is a type transformation (i.e. wrapper types) like text (wasm) -> #6.14(text) (rust) pub fn from_wasm_boundary_clone(&self, expr: &str, can_fail: bool) -> Vec { - //assert!(matches!(self, RustType::Tagged(_, _)) || !can_fail); + //assert!(matches!(self, Self::Tagged(_, _)) || !can_fail); match self { - RustType::Tagged(_tag, ty) => { - let mut inner = ty.from_wasm_boundary_clone(expr, can_fail); - if can_fail { - inner.push(ToWasmBoundaryOperations::TryInto); - } else { - inner.push(ToWasmBoundaryOperations::Into); - } - inner - }, - RustType::Rust(_ident) => vec![ + // Self::Tagged(_tag, ty) => { + // let mut inner = ty.from_wasm_boundary_clone(expr, can_fail); + // if can_fail { + // inner.push(ToWasmBoundaryOperations::TryInto); + // } else { + // inner.push(ToWasmBoundaryOperations::Into); + // } + // inner + // }, + Self::Rust(_ident) => vec![ ToWasmBoundaryOperations::Code(format!("{}.clone()", expr)), ToWasmBoundaryOperations::Into, ], - RustType::Alias(_ident, ty) => ty.from_wasm_boundary_clone(expr, can_fail), - RustType::Optional(ty) => ty.from_wasm_boundary_clone_optional(expr, can_fail), - RustType::Array(ty) => if self.directly_wasm_exposable() { - ty.from_wasm_boundary_clone(expr, can_fail) + Self::Alias(_ident, ty) => ty.from_wasm_boundary_clone(expr, can_fail), + Self::Optional(ty) => ty.conceptual_type.from_wasm_boundary_clone_optional(expr, can_fail), + Self::Array(ty) => if self.directly_wasm_exposable() { + ty.conceptual_type.from_wasm_boundary_clone(expr, can_fail) } else { vec![ ToWasmBoundaryOperations::Code(format!("{}.clone()", expr)), ToWasmBoundaryOperations::Into, ] }, - RustType::Map(_k, _v) => vec![ + Self::Map(_k, _v) => vec![ ToWasmBoundaryOperations::Code(format!("{}.clone()", expr)), ToWasmBoundaryOperations::Into, ], - RustType::CBORBytes(ty) => ty.from_wasm_boundary_clone(expr, can_fail), _ => vec![ToWasmBoundaryOperations::Code(expr.to_owned())], } } fn from_wasm_boundary_clone_optional(&self, expr: &str, can_fail: bool) -> Vec { - assert!(matches!(self, RustType::Tagged(_, _)) || !can_fail); + //assert!(matches!(self, Self::Tagged(_, _)) || !can_fail); match self { - RustType::Primitive(_p) => vec![ToWasmBoundaryOperations::Code(expr.to_owned())], - RustType::Tagged(_tag, ty) => { - let mut inner = ty.from_wasm_boundary_clone_optional(expr, can_fail); - if can_fail { - inner.push(ToWasmBoundaryOperations::TryInto); - } else { - inner.push(ToWasmBoundaryOperations::Into); - } - inner - }, - RustType::Alias(_ident, ty) => ty.from_wasm_boundary_clone_optional(expr, can_fail), - RustType::Array(..) | - RustType::Rust(..) | - RustType::Map(..) => vec![ + Self::Primitive(_p) => vec![ToWasmBoundaryOperations::Code(expr.to_owned())], + // Self::Tagged(_tag, ty) => { + // let mut inner = ty.from_wasm_boundary_clone_optional(expr, can_fail); + // if can_fail { + // inner.push(ToWasmBoundaryOperations::TryInto); + // } else { + // inner.push(ToWasmBoundaryOperations::Into); + // } + // inner + // }, + Self::Alias(_ident, ty) => ty.from_wasm_boundary_clone_optional(expr, can_fail), + Self::Array(..) | + Self::Rust(..) | + Self::Map(..) => vec![ ToWasmBoundaryOperations::Code(expr.to_owned()), if can_fail { ToWasmBoundaryOperations::MapTryInto @@ -944,7 +1048,6 @@ impl RustType { ToWasmBoundaryOperations::MapInto }, ], - RustType::CBORBytes(ty) => ty.from_wasm_boundary_clone_optional(expr, can_fail), _ => panic!("unsupported or unexpected"), } } @@ -952,17 +1055,15 @@ impl RustType { /// for non-owning parameter TYPES from wasm pub fn from_wasm_boundary_ref(&self, expr: &str) -> String { match self { - RustType::Tagged(_tag, ty) => ty.from_wasm_boundary_ref(expr), - RustType::Rust(_ident) => expr.to_owned(), - RustType::Alias(_ident, ty) => ty.from_wasm_boundary_ref(expr), - RustType::Optional(ty) => ty.from_wasm_boundary_ref(expr), - RustType::Array(ty) => if self.directly_wasm_exposable() { - ty.from_wasm_boundary_ref(expr) + Self::Rust(_ident) => expr.to_owned(), + Self::Alias(_ident, ty) => ty.from_wasm_boundary_ref(expr), + Self::Optional(ty) => ty.conceptual_type.from_wasm_boundary_ref(expr), + Self::Array(ty) => if self.directly_wasm_exposable() { + ty.conceptual_type.from_wasm_boundary_ref(expr) } else { expr.to_owned() }, - RustType::Map(_k, _v) => expr.to_owned(), - RustType::CBORBytes(ty) => ty.from_wasm_boundary_ref(expr), + Self::Map(_k, _v) => expr.to_owned(), _ => format!("&{}", expr), } } @@ -970,8 +1071,8 @@ impl RustType { /// FROM rust TO wasm (with cloning/wrapping) (for arguments) pub fn to_wasm_boundary(&self, expr: &str, is_ref: bool) -> String { match self { - RustType::Fixed(_) => panic!("fixed types are a serialization detail"), - RustType::Primitive(_p) => if self.is_copy() { + Self::Fixed(_) => panic!("fixed types are a serialization detail"), + Self::Primitive(_p) => if self.is_copy() { if is_ref { format!("*{}", expr) } else { @@ -980,20 +1081,18 @@ impl RustType { } else { format!("{}.clone()", expr) }, - RustType::Tagged(_tag, ty) => ty.to_wasm_boundary(expr, is_ref), - RustType::Rust(_ident) => format!("{}.clone().into()", expr), - //RustType::Array(ty) => format!("{}({}.clone())", ty.name_as_wasm_array(), expr), - //RustType::Map(k, v) => format!("{}({}.clone())", Self::name_for_wasm_map(k, v), expr), - RustType::Array(ty) => format!("{}.clone().into()", expr), - RustType::Map(k, v) => format!("{}.clone().into()", expr), - RustType::Optional(ty) => ty.to_wasm_boundary_optional(expr, is_ref), - RustType::Alias(_ident, ty) => ty.to_wasm_boundary(expr, is_ref), - RustType::CBORBytes(ty) => ty.to_wasm_boundary(expr, is_ref), + Self::Rust(_ident) => format!("{}.clone().into()", expr), + //Self::Array(ty) => format!("{}({}.clone())", ty.name_as_wasm_array(), expr), + //Self::Map(k, v) => format!("{}({}.clone())", Self::name_for_wasm_map(k, v), expr), + Self::Array(_ty) => format!("{}.clone().into()", expr), + Self::Map(_k, _v) => format!("{}.clone().into()", expr), + Self::Optional(ty) => ty.conceptual_type.to_wasm_boundary_optional(expr, is_ref), + Self::Alias(_ident, ty) => ty.to_wasm_boundary(expr, is_ref), } } /// FROM rust TO wasm as Option. This is separate as we can have optional fields - /// that act identical to RustType::Optional(ty) + /// that act identical to Self::Optional(ty) pub fn to_wasm_boundary_optional(&self, expr: &str, is_ref: bool) -> String { if self.directly_wasm_exposable() { self.to_wasm_boundary(expr, is_ref) @@ -1005,8 +1104,8 @@ impl RustType { // if it impements the Copy trait in rust pub fn is_copy(&self) -> bool { match self { - RustType::Fixed(_f) => unreachable!(), - RustType::Primitive(p) => match p { + Self::Fixed(_f) => unreachable!(), + Self::Primitive(p) => match p { Primitive::Bool | Primitive::I8 | Primitive::I16 | @@ -1020,13 +1119,11 @@ impl RustType { Primitive::Str | Primitive::Bytes => false, }, - RustType::Rust(_ident) => false, - RustType::Tagged(_tag, ty) => ty.is_copy(), - RustType::Array(_) => false, - RustType::Map(_k, _v) => false, - RustType::Optional(ty) => ty.is_copy(), - RustType::Alias(_ident, ty) => ty.is_copy(), - RustType::CBORBytes(ty) => ty.is_copy(), + Self::Rust(_ident) => false, + Self::Array(_) => false, + Self::Map(_k, _v) => false, + Self::Optional(ty) => ty.conceptual_type.is_copy(), + Self::Alias(_ident, ty) => ty.is_copy(), } } @@ -1038,58 +1135,10 @@ impl RustType { } } - // Ok case is single first cbor type in bytes, Err is multiple possibilities - pub fn cbor_types(&self) -> Vec { - match self { - RustType::Fixed(f) => vec![match f { - FixedValue::Uint(_) => CBORType::UnsignedInteger, - FixedValue::Nint(_) => CBORType::NegativeInteger, - FixedValue::Text(_) => CBORType::Text, - FixedValue::Null => CBORType::Special, - FixedValue::Bool(_) => CBORType::Special, - }], - RustType::Primitive(p) => p.cbor_types(), - RustType::Rust(_ident) => { - //panic!("TODO: store first cbor tag somewhere") - vec![CBORType::Array, CBORType::Map] - }, - RustType::Tagged(_tag, _ty) => vec![CBORType::Tag], - RustType::Array(_) => vec![CBORType::Array], - RustType::Map(_k, _v) => vec![CBORType::Map], - RustType::Optional(ty) => { - let mut inner_types = ty.cbor_types(); - if !inner_types.contains(&CBORType::Special) { - inner_types.push(CBORType::Special); - } - inner_types - }, - RustType::Alias(_ident, ty) => ty.cbor_types(), - RustType::CBORBytes(ty) => vec![CBORType::Bytes], - } - } - - fn _cbor_special_type(&self) -> Option { - unimplemented!() - } - - fn _is_serialize_multiline(&self) -> bool { - match self { - RustType::Fixed(_) => false, - RustType::Primitive(_) => false, - RustType::Rust(_) => false, - RustType::Array(_) => true, - RustType::Tagged(_, _) => true, - RustType::Optional(_) => false, - RustType::Map(_, _) => false, - RustType::Alias(_ident, ty) => ty._is_serialize_multiline(), - RustType::CBORBytes(ty) => true, - } - } - // CBOR len count for the entire type if it were embedded as a member in a cbor collection (array/map) pub fn expanded_field_count(&self, types: &IntermediateTypes) -> Option { match self { - RustType::Optional(ty) => match ty.expanded_field_count(types) { + Self::Optional(ty) => match ty.conceptual_type.expanded_field_count(types) { Some(1) => Some(1), // differing sizes when Null vs Some _ => None, @@ -1098,12 +1147,12 @@ impl RustType { // Once we split up parsing and codegen this shouldn't happen but with our current multi-pass // approach we might have out of order struct references which would break here without it // but on the final pass (the one we export) this should't be an issue - RustType::Rust(ident) => if types.is_plain_group(ident) { + Self::Rust(ident) => if types.is_plain_group(ident) { types.rust_structs.get(&ident)?.fixed_field_count(types) } else { Some(1) }, - RustType::Alias(_ident, ty) => ty.expanded_field_count(types), + Self::Alias(_ident, ty) => ty.expanded_field_count(types), _ => Some(1), } } @@ -1116,8 +1165,8 @@ impl RustType { match self.expanded_field_count(types) { Some(count) => Some(count.to_string()), None => match self { - RustType::Optional(ty) => Some(format!("match {} {{ Some(x) => {}, None => 1 }}", self_expr, ty.definite_info("x", types)?)), - RustType::Rust(ident) => if types.is_plain_group(ident) { + Self::Optional(ty) => Some(format!("match {} {{ Some(x) => {}, None => 1 }}", self_expr, ty.conceptual_type.definite_info("x", types)?)), + Self::Rust(ident) => if types.is_plain_group(ident) { match types.rust_structs.get(&ident) { Some(rs) => rs.definite_info(types), // when we split up parsing from codegen instead of multi-passing this should be an error @@ -1126,7 +1175,7 @@ impl RustType { } else { Some(String::from("1")) }, - RustType::Alias(_ident, ty) => ty.definite_info(self_expr, types), + Self::Alias(_ident, ty) => ty.definite_info(self_expr, types), _ => Some(String::from("1")), } } @@ -1137,11 +1186,11 @@ impl RustType { // has cbor len 1 too - to be consistent with expanded_field_count pub fn expanded_mandatory_field_count(&self, types: &IntermediateTypes) -> usize { match self { - RustType::Optional(ty) => match ty.expanded_field_count(types) { + Self::Optional(ty) => match ty.conceptual_type.expanded_field_count(types) { Some(1) => 1, _ => 0, }, - RustType::Rust(ident) => if types.is_plain_group(ident) { + Self::Rust(ident) => if types.is_plain_group(ident) { println!("ident: {}", ident); match types.rust_structs.get(&ident) { Some(x) => x.expanded_mandatory_field_count(types), @@ -1151,61 +1200,32 @@ impl RustType { } else { 1 }, - RustType::Alias(_ident, ty) => ty.expanded_mandatory_field_count(types), + Self::Alias(_ident, ty) => ty.expanded_mandatory_field_count(types), _ => 1, } } - pub fn visit_types(&self, types: &IntermediateTypes, f: &mut F) { + pub fn visit_types(&self, types: &IntermediateTypes, f: &mut F) { self.visit_types_excluding(types, f, &mut BTreeSet::new()) } - pub fn visit_types_excluding(&self, types: &IntermediateTypes, f: &mut F, already_visited: &mut BTreeSet) { + pub fn visit_types_excluding(&self, types: &IntermediateTypes, f: &mut F, already_visited: &mut BTreeSet) { f(self); match self { - RustType::Alias(_ident, ty) => ty.visit_types_excluding(types, f, already_visited), - RustType::Array(ty) => ty.visit_types_excluding(types, f, already_visited), - RustType::Fixed(..) => (), - RustType::Map(k, v) => { - k.visit_types_excluding(types, f, already_visited); - v.visit_types_excluding(types, f, already_visited); + Self::Alias(_ident, ty) => ty.visit_types_excluding(types, f, already_visited), + Self::Array(ty) => ty.conceptual_type.visit_types_excluding(types, f, already_visited), + Self::Fixed(_) => (), + Self::Map(k, v) => { + k.conceptual_type.visit_types_excluding(types, f, already_visited); + v.conceptual_type.visit_types_excluding(types, f, already_visited); }, - RustType::Optional(ty) => ty.visit_types_excluding(types, f, already_visited), - RustType::Primitive(..) => (), - RustType::Rust(ident) => { + Self::Optional(ty) => ty.conceptual_type.visit_types_excluding(types, f, already_visited), + Self::Primitive(_) => (), + Self::Rust(ident) => { if already_visited.insert(ident.clone()) { types.rust_struct(ident).map(|t| t.visit_types_excluding(types, f, already_visited)); } }, - RustType::Tagged(_tag, ty) => ty.visit_types_excluding(types, f, already_visited), - RustType::CBORBytes(ty) => ty.visit_types_excluding(types, f, already_visited), - } - } - - // This applies ONLY to how it's used around the generated code OUTSIDE of serialization contexts - pub fn strip_to_semantical_type(&self) -> &Self { - match self { - RustType::Alias(_ident, ty) => ty, - RustType::Tagged(_tag, ty) => ty, - RustType::CBORBytes(ty) => ty, - _ => self, - } - } - - // how things are handled in a CBOR/serialization context only - pub fn strip_to_serialization_type(&self) -> Self { - match self { - RustType::Alias(_ident, ty) => *ty.clone(), - RustType::CBORBytes(_ty) => RustType::Primitive(Primitive::Bytes), - _ => self.clone(), - } - } - - pub fn strip_tag(&self) -> &Self { - match self { - RustType::Alias(_ident, ty) => ty.strip_tag(), - RustType::Tagged(_tag, ty) => ty, - _ => self, } } } @@ -1534,20 +1554,20 @@ impl RustStruct { } } - pub fn visit_types(&self, types: &IntermediateTypes, f: &mut F) { + pub fn visit_types(&self, types: &IntermediateTypes, f: &mut F) { self.visit_types_excluding(types, f, &mut BTreeSet::new()) } - pub fn visit_types_excluding(&self, types: &IntermediateTypes, f: &mut F, already_visited: &mut BTreeSet) { + pub fn visit_types_excluding(&self, types: &IntermediateTypes, f: &mut F, already_visited: &mut BTreeSet) { match &self.variant { - RustStructType::Array{ element_type } => element_type.visit_types_excluding(types, f, already_visited), + RustStructType::Array{ element_type } => element_type.conceptual_type.visit_types_excluding(types, f, already_visited), RustStructType::GroupChoice{ variants, .. } | - RustStructType::TypeChoice{ variants, .. } => variants.iter().for_each(|v| v.rust_type.visit_types_excluding(types, f, already_visited)), - RustStructType::Record(record) => record.fields.iter().for_each(|field| field.rust_type.visit_types_excluding(types, f, already_visited)), + RustStructType::TypeChoice{ variants, .. } => variants.iter().for_each(|v| v.rust_type.conceptual_type.visit_types_excluding(types, f, already_visited)), + RustStructType::Record(record) => record.fields.iter().for_each(|field| field.rust_type.conceptual_type.visit_types_excluding(types, f, already_visited)), RustStructType::Table{domain, range} => { - domain.visit_types_excluding(types, f, already_visited); - range.visit_types_excluding(types, f, already_visited); + domain.conceptual_type.visit_types_excluding(types, f, already_visited); + range.conceptual_type.visit_types_excluding(types, f, already_visited); }, - RustStructType::Wrapper{ wrapped, .. } => wrapped.visit_types_excluding(types, f, already_visited), + RustStructType::Wrapper{ wrapped, .. } => wrapped.conceptual_type.visit_types_excluding(types, f, already_visited), RustStructType::Prelude => (), } } @@ -1568,7 +1588,7 @@ impl RustRecord { return None; } count += match self.rep { - Representation::Array => field.rust_type.expanded_field_count(types)?, + Representation::Array => field.rust_type.conceptual_type.expanded_field_count(types)?, Representation::Map => 1, }; } @@ -1588,20 +1608,20 @@ impl RustRecord { conditional_field_expr.push_str(" + "); } let (field_expr, field_contribution) = match self.rep { - Representation::Array => ("x", field.rust_type.definite_info("x", types)?), + Representation::Array => ("x", field.rust_type.conceptual_type.definite_info("x", types)?), // maps are defined by their keys instead (although they shouldn't have multi-length values either...) Representation::Map => ("_", String::from("1")), }; conditional_field_expr.push_str(&format!("match &self.{} {{ Some({}) => {}, None => 0 }}", field.name, field_expr, field_contribution)); } else { match self.rep { - Representation::Array => match field.rust_type.expanded_field_count(types) { + Representation::Array => match field.rust_type.conceptual_type.expanded_field_count(types) { Some(field_expanded_count) => fixed_field_count += field_expanded_count, None => { if !conditional_field_expr.is_empty() { conditional_field_expr.push_str(" + "); } - let field_len_expr = field.rust_type.definite_info(&format!("self.{}", field.name), types)?; + let field_len_expr = field.rust_type.conceptual_type.definite_info(&format!("self.{}", field.name), types)?; conditional_field_expr.push_str(&field_len_expr); }, }, @@ -1621,7 +1641,7 @@ impl RustRecord { } pub fn expanded_mandatory_field_count(&self, types: &IntermediateTypes) -> usize { - self.fields.iter().filter(|field| !field.optional).map(|field| field.rust_type.expanded_mandatory_field_count(types)).sum() + self.fields.iter().filter(|field| !field.optional).map(|field| field.rust_type.conceptual_type.expanded_mandatory_field_count(types)).sum() } pub fn cbor_len_info(&self, types: &IntermediateTypes) -> RustStructCBORLen { @@ -1729,7 +1749,7 @@ impl GenericInstance { } fn resolve_type(args: &BTreeMap<&RustIdent, &RustType>, orig: &RustType) -> RustType { - if let RustType::Rust(ident) = orig { + if let ConceptualRustType::Rust(ident) = &orig.conceptual_type { if let Some(resolved_type) = args.get(ident) { return (*resolved_type).clone(); } diff --git a/src/parsing.rs b/src/parsing.rs index d7dedbc..e10fdaf 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -6,6 +6,7 @@ use crate::comment_ast::{RuleMetadata, metadata_from_comments}; use crate::intermediate::{ AliasIdent, CDDLIdent, + ConceptualRustType, EnumVariant, FixedValue, GenericDef, @@ -120,8 +121,8 @@ fn parse_type_choices(types: &mut IntermediateTypes, name: &RustIdent, type_choi } let inner_rust_type = rust_type_from_type1(types, inner_type2); let final_type = match tag { - Some(tag) => RustType::Tagged(tag, Box::new(RustType::Optional(Box::new(inner_rust_type)))), - None => RustType::Optional(Box::new(inner_rust_type)), + Some(tag) => RustType::new(ConceptualRustType::Optional(Box::new(inner_rust_type))).tag(tag), + None => RustType::new(ConceptualRustType::Optional(Box::new(inner_rust_type))), }; types.register_type_alias(name.clone(), final_type, true, true); } else { @@ -164,20 +165,20 @@ fn parse_control_operator(types: &mut IntermediateTypes, parent: &Type2AndParent ControlOperator::Range((Some(range_start as i128), Some(if is_inclusive { range_end as i128 } else { (range_end + 1) as i128 }))) }, RangeCtlOp::CtlOp{ ctrl, .. } => match ctrl { - ".default" | - ".cborseq" | - ".within" | - ".and" => todo!("control operator {} not supported", ctrl), - ".cbor" => ControlOperator::CBOR(rust_type_from_type2(types, &Type2AndParent { type2: &operator.type2, parent: parent.parent, })), - ".eq" => ControlOperator::Range((Some(type2_to_number_literal(&operator.type2) as i128), Some(type2_to_number_literal(&operator.type2) as i128))), + cddl::token::ControlOperator::DEFAULT | + cddl::token::ControlOperator::CBORSEQ | + cddl::token::ControlOperator::WITHIN | + cddl::token::ControlOperator::AND => todo!("control operator {} not supported", ctrl), + cddl::token::ControlOperator::CBOR => ControlOperator::CBOR(rust_type_from_type2(types, &Type2AndParent { type2: &operator.type2, parent: parent.parent, })), + cddl::token::ControlOperator::EQ => ControlOperator::Range((Some(type2_to_number_literal(&operator.type2) as i128), Some(type2_to_number_literal(&operator.type2) as i128))), // TODO: this would be MUCH nicer (for error displaying, etc) to handle this in its own dedicated way // which might be necessary once we support other control operators anyway - ".ne" => ControlOperator::Range((Some((type2_to_number_literal(&operator.type2) + 1) as i128), Some((type2_to_number_literal(&operator.type2) - 1) as i128))), - ".le" => ControlOperator::Range((lower_bound, Some(type2_to_number_literal(&operator.type2) as i128))), - ".lt" => ControlOperator::Range((lower_bound, Some((type2_to_number_literal(&operator.type2) - 1) as i128))), - ".ge" => ControlOperator::Range((Some(type2_to_number_literal(&operator.type2) as i128), None)), - ".gt" => ControlOperator::Range((Some((type2_to_number_literal(&operator.type2) + 1) as i128), None)), - ".size" => { + cddl::token::ControlOperator::NE => ControlOperator::Range((Some((type2_to_number_literal(&operator.type2) + 1) as i128), Some((type2_to_number_literal(&operator.type2) - 1) as i128))), + cddl::token::ControlOperator::LE => ControlOperator::Range((lower_bound, Some(type2_to_number_literal(&operator.type2) as i128))), + cddl::token::ControlOperator::LT => ControlOperator::Range((lower_bound, Some((type2_to_number_literal(&operator.type2) - 1) as i128))), + cddl::token::ControlOperator::GE => ControlOperator::Range((Some(type2_to_number_literal(&operator.type2) as i128), None)), + cddl::token::ControlOperator::GT => ControlOperator::Range((Some((type2_to_number_literal(&operator.type2) + 1) as i128), None)), + cddl::token::ControlOperator::SIZE => { let base_range = match &operator.type2 { Type2::UintValue{ value, .. } => ControlOperator::Range((None, Some(*value as i128))), Type2::IntValue{ value, .. } => ControlOperator::Range((None, Some(*value as i128))), @@ -238,16 +239,16 @@ fn parse_control_operator(types: &mut IntermediateTypes, parent: &Type2AndParent } } -fn range_to_primitive(low: Option, high: Option) -> Option { +fn range_to_primitive(low: Option, high: Option) -> Option { match (low, high) { - (Some(l), Some(h)) if l == u8::MIN as i128 && h == u8::MAX as i128 => Some(RustType::Primitive(Primitive::U8)), - (Some(l), Some(h)) if l == i8::MIN as i128 && h == i8::MAX as i128 => Some(RustType::Primitive(Primitive::I8)), - (Some(l), Some(h)) if l == u16::MIN as i128 && h == u16::MAX as i128 => Some(RustType::Primitive(Primitive::U16)), - (Some(l), Some(h)) if l == i16::MIN as i128 && h == i16::MAX as i128 => Some(RustType::Primitive(Primitive::I16)), - (Some(l), Some(h)) if l == u32::MIN as i128 && h == u32::MAX as i128 => Some(RustType::Primitive(Primitive::U32)), - (Some(l), Some(h)) if l == i32::MIN as i128 && h == i32::MAX as i128 => Some(RustType::Primitive(Primitive::I32)), - (Some(l), Some(h)) if l == u64::MIN as i128 && h == u64::MAX as i128 => Some(RustType::Primitive(Primitive::U64)), - (Some(l), Some(h)) if l == i64::MIN as i128 && h == i64::MAX as i128 => Some(RustType::Primitive(Primitive::I64)), + (Some(l), Some(h)) if l == u8::MIN as i128 && h == u8::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::U8)), + (Some(l), Some(h)) if l == i8::MIN as i128 && h == i8::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::I8)), + (Some(l), Some(h)) if l == u16::MIN as i128 && h == u16::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::U16)), + (Some(l), Some(h)) if l == i16::MIN as i128 && h == i16::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::I16)), + (Some(l), Some(h)) if l == u32::MIN as i128 && h == u32::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::U32)), + (Some(l), Some(h)) if l == i32::MIN as i128 && h == i32::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::I32)), + (Some(l), Some(h)) if l == u64::MIN as i128 && h == u64::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::U64)), + (Some(l), Some(h)) if l == i64::MIN as i128 && h == i64::MAX as i128 => Some(ConceptualRustType::Primitive(Primitive::I64)), _ => None } } @@ -265,32 +266,32 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: assert!(generic_params.is_none(), "Generics combined with range specifiers not supported"); // TODO: what about aliases that resolve to these? is it even possible to know this at this stage? let field_type = || match cddl_ident.to_string().as_str() { - "tstr" | "text" => RustType::Primitive(Primitive::Str), - "bstr" | "bytes" => RustType::Primitive(Primitive::Bytes), + "tstr" | "text" => ConceptualRustType::Primitive(Primitive::Str), + "bstr" | "bytes" => ConceptualRustType::Primitive(Primitive::Bytes), other => panic!("range control specifiers not supported for type: {}", other), }; match control { ControlOperator::Range(min_max) => { match cddl_ident.to_string().as_str() { "int" | "uint" => match range_to_primitive(min_max.0, min_max.1) { - Some(t) => types.register_type_alias(type_name.clone(), t, true, true), + Some(t) => types.register_type_alias(type_name.clone(), t.into(), true, true), None => panic!("unsupported range for {:?}: {:?}", cddl_ident.to_string().as_str(), control) }, - _ => types.register_rust_struct(RustStruct::new_wrapper(type_name.clone(), outer_tag, field_type().clone(), Some(min_max))) + _ => types.register_rust_struct(RustStruct::new_wrapper(type_name.clone(), outer_tag, field_type().clone().into(), Some(min_max))) } }, ControlOperator::CBOR(ty) => match field_type() { - RustType::Primitive(Primitive::Bytes) => { - types.register_type_alias(type_name.clone(), RustType::CBORBytes(Box::new(ty)), true, true); + ConceptualRustType::Primitive(Primitive::Bytes) => { + types.register_type_alias(type_name.clone(), ty.as_bytes(), true, true); }, _ => panic!(".cbor is only allowed on bytes as per CDDL spec"), }, } }, None => { - let concrete_type = match types.new_type(&cddl_ident) { - RustType::Alias(_ident, ty) => *ty, - ty => ty, + let mut concrete_type = types.new_type(&cddl_ident); + if let ConceptualRustType::Alias(_ident, ty) = concrete_type.conceptual_type { + concrete_type.conceptual_type = *ty; }; match &generic_params { Some(_params) => { @@ -347,18 +348,18 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: let new_type = types.new_type(&CDDLIdent::new(ident.to_string())); let control = inner_type.type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: &inner_type.type1, type2: &inner_type.type1.type2 }, op)); match control { - Some(ControlOperator::CBOR(ty)) => { + Some(ControlOperator::CBOR(_ty)) => { // TODO: this would be fixed if we ordered definitions via a dependency graph to begin with // which would also allow us to do a single pass instead of many like we do now let base_type = types .apply_type_aliases(&AliasIdent::new(CDDLIdent::new(ident.to_string()))) .expect(&format!("Please move definition for {} above {}", type_name, ident)); - types.register_type_alias(type_name.clone(), RustType::Tagged(tag_unwrap, Box::new(RustType::CBORBytes(Box::new(base_type)))), true, true); + types.register_type_alias(type_name.clone(), base_type.as_bytes().tag(tag_unwrap), true, true); }, Some(ControlOperator::Range(min_max)) => { match ident.to_string().as_str() { "int" | "uint" => match range_to_primitive(min_max.0, min_max.1) { - Some(t) => types.register_type_alias(type_name.clone(), t, true, true), + Some(t) => types.register_type_alias(type_name.clone(), t.into(), true, true), None => panic!("unsupported range for {:?}: {:?}", ident.to_string().as_str(), control) }, _ => types.register_rust_struct(RustStruct::new_wrapper(type_name.clone(), *tag, new_type, Some(min_max))) @@ -370,7 +371,7 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: let base_type = types .apply_type_aliases(&AliasIdent::new(CDDLIdent::new(ident.to_string()))) .expect(&format!("Please move definition for {} above {}", type_name, ident)); - types.register_type_alias(type_name.clone(), RustType::Tagged(tag_unwrap, Box::new(base_type)), true, true); + types.register_type_alias(type_name.clone(), base_type.tag(tag_unwrap), true, true); }, } }, @@ -383,7 +384,7 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: }, // Note: bool constants are handled via Type2::Typename Type2::IntValue{ value, .. } => { - let fallback_type = RustType::Fixed(FixedValue::Nint(*value)); + let fallback_type = ConceptualRustType::Fixed(FixedValue::Nint(*value)); let control = type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: type1, type2: &type1.type2 }, op)); let base_type = match control { @@ -395,10 +396,10 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: }, _ => fallback_type }; - types.register_type_alias(type_name.clone(), base_type, true, true); + types.register_type_alias(type_name.clone(), base_type.into(), true, true); }, Type2::UintValue{ value, .. } => { - let fallback_type = RustType::Fixed(FixedValue::Uint(*value)); + let fallback_type = ConceptualRustType::Fixed(FixedValue::Uint(*value)); let control = type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: type1, type2: &type1.type2 }, op)); let base_type = match control { @@ -410,10 +411,10 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: }, _ => fallback_type }; - types.register_type_alias(type_name.clone(), base_type, true, true); + types.register_type_alias(type_name.clone(), base_type.into(), true, true); }, Type2::TextValue{ value, .. } => { - types.register_type_alias(type_name.clone(), RustType::Fixed(FixedValue::Text(value.to_string())), true, true); + types.register_type_alias(type_name.clone(), ConceptualRustType::Fixed(FixedValue::Text(value.to_string())).into(), true, true); }, x => { panic!("\nignored typename {} -> {:?}\n", type_name, x); @@ -600,11 +601,11 @@ fn rust_type_from_type1(types: &mut IntermediateTypes, type1: &Type1) -> RustTyp let control = type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: type1, type2: &type1.type2 }, op)); // println!("type1: {:#?}", type1); match control { - Some(ControlOperator::CBOR(ty)) => RustType::CBORBytes(Box::new(ty)), + Some(ControlOperator::CBOR(ty)) => ty.as_bytes(), Some(ControlOperator::Range(min_max)) => { match &type1.type2 { Type2::Typename{ ident, .. } if ident.to_string() == "uint" || ident.to_string() == "int" => match range_to_primitive(min_max.0, min_max.1) { - Some(t) => t, + Some(t) => t.into(), None => panic!("unsupported range for {:?}: {:?}", ident.to_string().as_str(), control) }, _ => rust_type_from_type2(types, &Type2AndParent { type2: &type1.type2, parent: type1, }) @@ -617,10 +618,10 @@ fn rust_type_from_type1(types: &mut IntermediateTypes, type1: &Type1) -> RustTyp fn rust_type_from_type2(types: &mut IntermediateTypes, type2: &Type2AndParent) -> RustType { // TODO: socket plugs (used in hash type) match &type2.type2 { - Type2::UintValue{ value, .. } => RustType::Fixed(FixedValue::Uint(*value)), - Type2::IntValue{ value, .. } => RustType::Fixed(FixedValue::Nint(*value)), - //Type2::FloatValue{ value, .. } => RustType::Fixed(FixedValue::Float(*value)), - Type2::TextValue{ value, .. } => RustType::Fixed(FixedValue::Text(value.to_string())), + Type2::UintValue{ value, .. } => ConceptualRustType::Fixed(FixedValue::Uint(*value)).into(), + Type2::IntValue{ value, .. } => ConceptualRustType::Fixed(FixedValue::Nint(*value)).into(), + //Type2::FloatValue{ value, .. } => ConceptualRustType::Fixed(FixedValue::Float(*value)), + Type2::TextValue{ value, .. } => ConceptualRustType::Fixed(FixedValue::Text(value.to_string())).into(), Type2::Typename{ ident, generic_args, .. } => { let cddl_ident = CDDLIdent::new(ident.ident); match generic_args { @@ -672,7 +673,7 @@ fn rust_type_from_type2(types: &mut IntermediateTypes, type2: &Type2AndParent) - //let array_wrapper_name = element_type.name_as_wasm_array(); //types.create_and_register_array_type(element_type, &array_wrapper_name) - RustType::Array(Box::new(element_type)) + ConceptualRustType::Array(Box::new(element_type)).into() }, Type2::Map { group, .. } => { match group.group_choices.len() { @@ -690,7 +691,7 @@ fn rust_type_from_type2(types: &mut IntermediateTypes, type2: &Type2AndParent) - // for general map types we can though but not for tables //let table_type_ident = RustIdent::new(CDDLIdent::new(format!("Map{}To{}", key_type.for_wasm_member(), value_type.for_wasm_member()))); //types.register_rust_struct(RustStruct::new_table(table_type_ident, None, key_type.clone(), value_type.clone())); - RustType::Map(Box::new(key_type), Box::new(value_type)) + ConceptualRustType::Map(Box::new(key_type), Box::new(value_type)).into() }, None => unimplemented!("TODO: non-table types as types: {:?}", group), } @@ -700,7 +701,8 @@ fn rust_type_from_type2(types: &mut IntermediateTypes, type2: &Type2AndParent) - }, // unsure if we need to handle the None case - when does this happen? Type2::TaggedData{ tag, t, .. } => { - RustType::Tagged(tag.expect("tagged data without tag not supported"), Box::new(rust_type(types, t))) + let tag_unwrap = tag.expect("tagged data without tag not supported"); + rust_type(types, t).tag(tag_unwrap) }, _ => { panic!("Ignoring Type2: {:?}", type2.type2); @@ -717,10 +719,10 @@ fn rust_type(types: &mut IntermediateTypes, t: &Type) -> RustType { let a = &t.type_choices[0].type1; let b = &t.type_choices[1].type1; if type2_is_null(&a.type2) { - return RustType::Optional(Box::new(rust_type_from_type1(types, b))); + return ConceptualRustType::Optional(Box::new(rust_type_from_type1(types, b))).into(); } if type2_is_null(&b.type2) { - return RustType::Optional(Box::new(rust_type_from_type1(types, a))); + return ConceptualRustType::Optional(Box::new(rust_type_from_type1(types, a))).into(); } } let variants = create_variants_from_type_choices(types, &t.type_choices); @@ -806,7 +808,7 @@ fn parse_record_from_group_choice(types: &mut IntermediateTypes, rep: Representa let field_name = group_entry_to_field_name(group_entry, index, &mut generated_fields, optional_comma); // does not exist for fixed values importantly let field_type = group_entry_to_type(types, group_entry); - if let RustType::Rust(ident) = &field_type { + if let ConceptualRustType::Rust(ident) = &field_type.conceptual_type { types.set_rep_if_plain_group(ident, rep); } let optional_field = group_entry_optional(group_entry); @@ -871,7 +873,7 @@ pub fn parse_group(types: &mut IntermediateTypes, group: &Group, name: &RustIden if group_choice.group_entries.len() == 1 { let group_entry = &group_choice.group_entries.first().unwrap().0; let ty = group_entry_to_type(types, group_entry); - let serialize_as_embedded = if let RustType::Rust(ident) = &ty { + let serialize_as_embedded = if let ConceptualRustType::Rust(ident) = &ty.conceptual_type { // we might need to generate it if not used elsewhere types.set_rep_if_plain_group(ident, rep); types.is_plain_group(ident) @@ -901,7 +903,7 @@ pub fn parse_group(types: &mut IntermediateTypes, group: &Group, name: &RustIden let variant_name = RustIdent::new(CDDLIdent::new(ident_name)); types.mark_plain_group(variant_name.clone(), None); parse_group_choice(types, group_choice, &variant_name, rep, None, generic_params.clone()); - EnumVariant::new(VariantIdent::new_rust(variant_name.clone()), RustType::Rust(variant_name), true) + EnumVariant::new(VariantIdent::new_rust(variant_name.clone()), ConceptualRustType::Rust(variant_name).into(), true) } }).collect(); types.register_rust_struct(RustStruct::new_group_choice(name.clone(), tag, variants, rep)); From a995bad135096b966eeb8e2203d66a73eff26443 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 1 Nov 2022 13:30:59 -0700 Subject: [PATCH 2/2] .default support Adds .default support to typenames (`foo = bar .default baz`) and to map fields (`foo = { ? 1 => bar .default baz }`). This is not applied into nested contexts e.g.: ``` foo = uint .default 2 bar = { * str => foo } ``` as how this should be handled is extremely ambiguous. Should this result in an extra encoding variable which is a map e.g. `default_included: BTreeSet` and having the behavior be to omit it if it's not included in this encoding var and it's equal to the default ? As per the CDDL RFC we only apply it to optional fields: ``` This control is only meaningful when the control type is used in an optional context; otherwise, there would be no way to make use of the default value. ``` We support encoding preservation for round-tripping i.e. if a default value is explicitly present it will remain explicitly present. With `preserve-encodings=false` it will be omitted if the value present is equal to the default value. No changes were done with canonical encodings as the RFC doesn't seem to mention how that should be handled as canonical encodings are specified in the CBOR RFC not the CDDL one, and `.default` is only present in the CDDL one. It's that possible we could omit fields equal to the default if needed but we need to confirm this first. Optional fields with `.default` are no longer encoded as `Option` but instead just `T` now which should help with usability. The field will default to the default via `new()` and deserialization. Fixes #42 --- src/generation.rs | 140 +++++++++++++++++++--------- src/intermediate.rs | 91 +++++++++++++----- src/parsing.rs | 34 ++++++- tests/core/input.cddl | 9 +- tests/core/tests.rs | 10 ++ tests/preserve-encodings/input.cddl | 9 +- tests/preserve-encodings/tests.rs | 44 +++++++++ 7 files changed, 261 insertions(+), 76 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index faf1768..1739a5e 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -713,7 +713,7 @@ impl GenerationScope { start_len(body, Representation::Array, serializer_use, &encoding_var, &format!("{}.len() as u64", config.expr)); let elem_var_name = format!("{}_elem", config.var_name); let elem_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&elem_var_name, (&ty.clone().resolve_aliases()).into()) + encoding_fields(&elem_var_name, &ty.clone().resolve_aliases(), false) } else { vec![] }; @@ -739,8 +739,8 @@ impl GenerationScope { SerializingRustType::Root(ConceptualRustType::Map(key, value)) => { start_len(body, Representation::Map, serializer_use, &encoding_var, &format!("{}.len() as u64", config.expr)); let ser_loop = if CLI_ARGS.preserve_encodings { - let key_enc_fields = encoding_fields(&format!("{}_key", config.var_name), (&key.clone().resolve_aliases()).into()); - let value_enc_fields = encoding_fields(&format!("{}_value", config.var_name), (&value.clone().resolve_aliases()).into()); + let key_enc_fields = encoding_fields(&format!("{}_key", config.var_name), &key.clone().resolve_aliases(), false); + let value_enc_fields = encoding_fields(&format!("{}_value", config.var_name), &value.clone().resolve_aliases(), false); let mut ser_loop = if CLI_ARGS.canonical_form { let mut key_order = codegen::Block::new(&format!("let mut key_order = {}.iter().map(|(k, v)|", config.expr)); key_order @@ -1074,7 +1074,7 @@ impl GenerationScope { } } let ty_enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(var_name, (&ty.clone().resolve_aliases()).into()) + encoding_fields(var_name, &ty.clone().resolve_aliases(), false) } else { vec![] }; @@ -1140,7 +1140,7 @@ impl GenerationScope { body.line(&format!("let mut {} = Vec::new();", arr_var_name)); let elem_var_name = format!("{}_elem", var_name); let elem_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&elem_var_name, (&ty.clone().resolve_aliases()).into()) + encoding_fields(&elem_var_name, &ty.clone().resolve_aliases(), false) } else { vec![] }; @@ -1200,12 +1200,12 @@ impl GenerationScope { let key_var_name = format!("{}_key", var_name); let value_var_name = format!("{}_value", var_name); let key_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&key_var_name, (&key_type.clone().resolve_aliases()).into()) + encoding_fields(&key_var_name, &key_type.clone().resolve_aliases(), false) } else { vec![] }; let value_encs = if CLI_ARGS.preserve_encodings { - encoding_fields(&value_var_name, (&value_type.clone().resolve_aliases()).into()) + encoding_fields(&value_var_name, &value_type.clone().resolve_aliases(), false) } else { vec![] }; @@ -1356,11 +1356,6 @@ impl GenerationScope { // new for variant in variants.iter() { let variant_arg = variant.name_as_var(); - let enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(&variant_arg, (&variant.rust_type.clone().resolve_aliases()).into()) - } else { - vec![] - }; let mut new_func = codegen::Function::new(&format!("new_{}", variant_arg)); new_func.vis("pub"); let can_fail = match &variant.name { @@ -2154,7 +2149,22 @@ fn key_encoding_field(name: &str, key: &FixedValue) -> EncodingField { } } -fn encoding_fields(name: &str, ty: SerializingRustType) -> Vec { +fn encoding_fields(name: &str, ty: &RustType, include_default: bool) -> Vec { + assert!(CLI_ARGS.preserve_encodings); + // TODO: how do we handle defaults for nested things? e.g. inside of a ConceptualRustType::Map + let mut encs = encoding_fields_impl(name, ty.into()); + if include_default && ty.default.is_some() { + encs.push(EncodingField { + field_name: format!("{}_default_present", name), + type_name: "bool".to_owned(), + default_expr: "false", + inner: Vec::new(), + }); + } + encs +} + +fn encoding_fields_impl(name: &str, ty: SerializingRustType) -> Vec { assert!(CLI_ARGS.preserve_encodings); match ty { SerializingRustType::Root(ConceptualRustType::Array(elem_ty)) => { @@ -2164,7 +2174,7 @@ fn encoding_fields(name: &str, ty: SerializingRustType) -> Vec { default_expr: "LenEncoding::default()", inner: Vec::new(), }; - let inner_encs = encoding_fields(&format!("{}_elem", name), (&**elem_ty).into()); + let inner_encs = encoding_fields_impl(&format!("{}_elem", name), (&**elem_ty).into()); if inner_encs.is_empty() { vec![base] } else { @@ -2193,8 +2203,8 @@ fn encoding_fields(name: &str, ty: SerializingRustType) -> Vec { inner: Vec::new(), } ]; - let key_encs = encoding_fields(&format!("{}_key", name), (&**k).into()); - let val_encs = encoding_fields(&format!("{}_value", name), (&**v).into()); + let key_encs = encoding_fields_impl(&format!("{}_key", name), (&**k).into()); + let val_encs = encoding_fields_impl(&format!("{}_value", name), (&**v).into()); if !key_encs.is_empty() { let type_name_value = if key_encs.len() == 1 { @@ -2256,21 +2266,21 @@ fn encoding_fields(name: &str, ty: SerializingRustType) -> Vec { SerializingRustType::Root(ConceptualRustType::Fixed(f)) => match f { FixedValue::Bool(_) | FixedValue::Null => vec![], - FixedValue::Nint(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::I64)).into()), - FixedValue::Uint(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::U64)).into()), - FixedValue::Text(_) => encoding_fields(name, (&ConceptualRustType::Primitive(Primitive::Str)).into()), + FixedValue::Nint(_) => encoding_fields_impl(name, (&ConceptualRustType::Primitive(Primitive::I64)).into()), + FixedValue::Uint(_) => encoding_fields_impl(name, (&ConceptualRustType::Primitive(Primitive::U64)).into()), + FixedValue::Text(_) => encoding_fields_impl(name, (&ConceptualRustType::Primitive(Primitive::Str)).into()), }, SerializingRustType::Root(ConceptualRustType::Alias(_, _)) => panic!("resolve types before calling this"), - SerializingRustType::Root(ConceptualRustType::Optional(ty)) => encoding_fields(name, (&**ty).into()), + SerializingRustType::Root(ConceptualRustType::Optional(ty)) => encoding_fields(name, ty, false), SerializingRustType::Root(ConceptualRustType::Rust(_)) => vec![], SerializingRustType::EncodingOperation(CBOREncodingOperation::Tagged(tag), child) => { - let mut encs = encoding_fields(&format!("{}_tag", name), (&ConceptualRustType::Fixed(FixedValue::Uint(*tag))).into()); - encs.append(&mut encoding_fields(name, *child)); + let mut encs = encoding_fields_impl(&format!("{}_tag", name), (&ConceptualRustType::Fixed(FixedValue::Uint(*tag))).into()); + encs.append(&mut encoding_fields_impl(name, *child)); encs }, SerializingRustType::EncodingOperation(CBOREncodingOperation::CBORBytes, child) => { - let mut encs = encoding_fields(&format!("{}_bytes", name), (&ConceptualRustType::Primitive(Primitive::Bytes)).into()); - encs.append(&mut encoding_fields(name, *child)); + let mut encs = encoding_fields_impl(&format!("{}_bytes", name), (&ConceptualRustType::Primitive(Primitive::Bytes)).into()); + encs.append(&mut encoding_fields_impl(name, *child)); encs }, } @@ -2284,7 +2294,7 @@ fn encoding_var_names_str(field_name: &str, rust_type: &RustType) -> String { } else { vec![field_name.to_owned()] }; - for enc in encoding_fields(field_name, (&resolved_rust_type).into()).into_iter() { + for enc in encoding_fields(field_name, &resolved_rust_type, false).into_iter() { var_names.push(enc.field_name); } let var_names_str = if var_names.len() > 1 { @@ -2321,20 +2331,35 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na setter .arg_mut_self() .arg(&field.name, &field.rust_type.for_wasm_param()) - .vis("pub") - .line(format!( + .vis("pub"); + if field.rust_type.default.is_some() { + setter.line(format!( + "self.0.{} = {}", + field.name, + ToWasmBoundaryOperations::format(field.rust_type.from_wasm_boundary_clone(&field.name, false).into_iter()))); + } else { + setter.line(format!( "self.0.{} = Some({})", field.name, ToWasmBoundaryOperations::format(field.rust_type.from_wasm_boundary_clone(&field.name, false).into_iter()))); + } + // ^ TODO: check types.can_new_fail(&field.name) wrapper.s_impl.push_fn(setter); // getter let mut getter = codegen::Function::new(&field.name); getter .arg_ref_self() - .ret(format!("Option<{}>", field.rust_type.for_wasm_return())) - .vis("pub") - .line(field.rust_type.to_wasm_boundary_optional(&format!("self.0.{}", field.name), false)); + .vis("pub"); + if field.rust_type.default.is_some() { + getter + .ret(field.rust_type.for_wasm_return()) + .line(field.rust_type.to_wasm_boundary(&format!("self.0.{}", field.name), false)); + } else { + getter + .ret(format!("Option<{}>", field.rust_type.for_wasm_return())) + .line(field.rust_type.to_wasm_boundary_optional(&format!("self.0.{}", field.name), false)); + } wrapper.s_impl.push_fn(getter); } else { // new @@ -2374,7 +2399,12 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } // Fixed values only exist in (de)serialization code (outside of preserve-encodings=true) if !field.rust_type.is_fixed_value() { - if field.optional { + if let Some(default_value) = &field.rust_type.default { + // field + native_struct.field(&format!("pub {}", field.name), field.rust_type.for_rust_member(false)); + // new + native_new_block.line(format!("{}: {},", field.name, default_value.to_primitive_str_assign())); + } else if field.optional { // field native_struct.field(&format!("pub {}", field.name), format!("Option<{}>", field.rust_type.for_rust_member(false))); // new @@ -2403,7 +2433,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } for field in &record.fields { // even fixed values still need to keep track of their encodings - for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { + for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases(), true) { encoding_struct.field(&format!("pub {}", field_enc.field_name), field_enc.type_name); } if record.rep == Representation::Map { @@ -2493,12 +2523,21 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na let mut deser_ret = Block::new(&format!("Ok({}", name)); for field in record.fields.iter() { if field.optional { - let mut optional_array_ser_block = Block::new(&format!("if let Some(field) = &self.{}", field.name)); + let (optional_field_check, field_expr, expr_is_ref) = if let Some(default_value) = &field.rust_type.default { + (if CLI_ARGS.preserve_encodings { + format!("if self.{} != {} || self.encodings.map(|encs| encs.{}_default_present).unwrap_or(false)", field.name, default_value.to_primitive_str_compare(), field.name) + } else { + format!("if self.{} != {}", field.name, default_value.to_primitive_str_compare()) + }, format!("self.{}", field.name), false) + } else { + (format!("if let Some(field) = &self.{}", field.name), "field".to_owned(), true) + }; + let mut optional_array_ser_block = Block::new(&optional_field_check); gen_scope.generate_serialize( types, (&field.rust_type).into(), &mut optional_array_ser_block, - SerializeConfig::new("field", &field.name).expr_is_ref(true).encoding_var_in_option_struct("self.encodings")); + SerializeConfig::new(&field_expr, &field.name).expr_is_ref(expr_is_ref).encoding_var_in_option_struct("self.encodings")); ser_func.push_block(optional_array_ser_block); gen_scope.dont_generate_deserialize(name, format!("Array with optional field {}: {}", field.name, field.rust_type.for_rust_member(false))); } else { @@ -2552,7 +2591,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na for field in record.fields.iter() { // we don't support deserialization for optional fields so don't even bother if !field.optional { - for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { + for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases(), true) { encoding_ctor.line(format!("{},", field_enc.field_name)); } } @@ -2589,7 +2628,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } // declare variables for deser loop if CLI_ARGS.preserve_encodings { - for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { + for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases(), true) { deser_body.line(&format!("let mut {} = {};", field_enc.field_name, field_enc.default_expr)); } let key_enc = key_encoding_field(&field.name, &field.key.as_ref().unwrap()); @@ -2600,7 +2639,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { deser_body.line(&format!("let mut {} = None;", field.name)); } - let (data_name, expr_is_ref) = if field.optional { + let (data_name, expr_is_ref) = if field.optional && field.rust_type.default.is_none() { (String::from("field"), true) } else { (format!("self.{}", field.name), false) @@ -2634,7 +2673,6 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na let temp_var_prefix = format!("tmp_{}", field.name); let var_names_str = encoding_var_names_str(&temp_var_prefix, &field.rust_type); - let needs_vars = !var_names_str.is_empty(); let (before, after) = if var_names_str.is_empty() { ("".to_owned(), "?") } else { @@ -2659,7 +2697,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { deser_block.line(format!("{} = Some(tmp_{});", field.name, field.name)); } - for enc_field in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { + for enc_field in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases(), false) { deser_block.line(format!("{} = tmp_{};", enc_field.field_name, enc_field.field_name)); } } else { @@ -2757,7 +2795,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na // ser_loop_match.line(format!("{} => {},")); //} else { //} - let mut field_ser_block = if field.optional { + let mut field_ser_block = if field.optional && field.rust_type.default.is_none() { Block::new(&format!("{} => if let Some(field) = &self.{}", field_index, field.name)) } else { Block::new(&format!("{} =>", field_index)) @@ -2773,7 +2811,12 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { for (_field_index, field, content) in ser_content.into_iter() { if field.optional { - let mut optional_ser_field = Block::new(&format!("if let Some(field) = &self.{}", field.name)); + let optional_ser_field_check = if let Some(default_value) = &field.rust_type.default { + format!("if self.{} != {}", field.name, default_value.to_primitive_str_compare()) + } else { + format!("if let Some(field) = &self.{}", field.name) + }; + let mut optional_ser_field = Block::new(&optional_ser_field_check); optional_ser_field.push_all(content); ser_func.push_block(optional_ser_field); } else { @@ -2854,11 +2897,18 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na } else { let mut mandatory_field_check = Block::new(&format!("let {} = match {}", field.name, field.name)); mandatory_field_check.line("Some(x) => x,"); - + mandatory_field_check.line(format!("None => return Err(DeserializeFailure::MandatoryFieldMissing({}).into()),", key)); mandatory_field_check.after(";"); deser_body.push_block(mandatory_field_check); } + } else if let Some(default_value) = &field.rust_type.default { + if CLI_ARGS.preserve_encodings { + let mut default_present_check = Block::new(&format!("if {} == Some({})", field.name, default_value.to_primitive_str_assign())); + default_present_check.line(format!("{}_default_present = true;", field.name)); + deser_body.push_block(default_present_check); + } + deser_body.line(&format!("let {} = {}.unwrap_or({});", field.name, field.name, default_value.to_primitive_str_assign())); } if !field.rust_type.is_fixed_value() { ctor_block.line(format!("{},", field.name)); @@ -2875,7 +2925,7 @@ fn codegen_struct(gen_scope: &mut GenerationScope, types: &IntermediateTypes, na for field in record.fields.iter() { let key_enc = key_encoding_field(&field.name, field.key.as_ref().unwrap()); encoding_ctor.line(format!("{},", key_enc.field_name)); - for field_enc in encoding_fields(&field.name, (&field.rust_type.clone().resolve_aliases()).into()) { + for field_enc in encoding_fields(&field.name, &field.rust_type.clone().resolve_aliases(), true) { encoding_ctor.line(format!("{},", field_enc.field_name)); } } @@ -3078,7 +3128,7 @@ impl EnumVariantInRust { fn new(variant: &EnumVariant, rep: Option) -> Self { let name = variant.name_as_var(); let mut enc_fields = if CLI_ARGS.preserve_encodings { - encoding_fields(&name, (&variant.rust_type.clone().resolve_aliases()).into()) + encoding_fields(&name, &variant.rust_type.clone().resolve_aliases(), true) } else { vec![] }; @@ -3547,7 +3597,7 @@ fn generate_wrapper_struct(gen_scope: &mut GenerationScope, types: &Intermediate let encoding_name = RustIdent::new(CDDLIdent::new(format!("{}Encoding", type_name))); let enc_fields = if CLI_ARGS.preserve_encodings { s.field("pub inner", field_type.for_rust_member(false)); - let enc_fields = encoding_fields("inner", (&field_type.clone().resolve_aliases()).into()); + let enc_fields = encoding_fields("inner", &field_type.clone().resolve_aliases(), true); if !enc_fields.is_empty() { s.field(&format!("{}pub encodings", encoding_var_macros(types.used_as_key(type_name))), format!("Option<{}>", encoding_name)); diff --git a/src/intermediate.rs b/src/intermediate.rs index 631e599..8ab418c 100644 --- a/src/intermediate.rs +++ b/src/intermediate.rs @@ -435,6 +435,27 @@ impl FixedValue { }.expect("Unable to serialize key for canonical ordering"); buf.finalize() } + + /// Converts a literal to a valid rust expression capable of initializing a Primitive + /// e.g. Text is an actual String, etc + pub fn to_primitive_str_assign(&self) -> String { + match self { + FixedValue::Null => "None".to_owned(), + FixedValue::Bool(b) => b.to_string(), + FixedValue::Nint(i) => i.to_string(), + FixedValue::Uint(u) => u.to_string(), + FixedValue::Text(s) => format!("\"{}\".to_owned()", s), + } + } + + /// Converts a literal to a valid rust comparison valid for comparisons + /// e.g. Text can be &str to avoid creating a String + pub fn to_primitive_str_compare(&self) -> String { + match self { + FixedValue::Text(s) => format!("\"{}\"", s), + _=> self.to_primitive_str_assign(), + } + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -498,6 +519,7 @@ impl Primitive { }) } + /// All POSSIBLE outermost CBOR types this can encode to pub fn cbor_types(&self) -> Vec { match self { Primitive::Bool => vec![CBORType::Special], @@ -689,7 +711,20 @@ impl RustType { pub fn default(mut self, default_value: FixedValue) -> Self { assert!(self.default.is_none()); - // TODO: verify that the fixed value makes sense for the conceptual_type + let matches = if let ConceptualRustType::Primitive(p) = self.conceptual_type.clone().resolve_aliases() { + match &default_value { + FixedValue::Bool(_) => p == Primitive::Bool, + FixedValue::Nint(_) => p.cbor_types().contains(&CBORType::NegativeInteger), + FixedValue::Uint(_) => p.cbor_types().contains(&CBORType::UnsignedInteger), + FixedValue::Null => false, + FixedValue::Text(_) => p == Primitive::Str, + } + } else { + false + }; + if !matches { + panic!(".default {:?} invalid for type {:?}", default_value, self.conceptual_type); + } self.default = Some(default_value); self } @@ -707,6 +742,7 @@ impl RustType { } } + /// All POSSIBLE outermost CBOR types this can encode to pub fn cbor_types(&self) -> Vec { match self.encodings.last() { Some(CBOREncodingOperation::Tagged(_)) => vec![CBORType::Tag], @@ -970,7 +1006,7 @@ impl ConceptualRustType { } } - /// IDENTIFIER for an enum variant. (Use for_rust_member() for the ) + /// IDENTIFIER for an enum variant. (Use for_rust_member() for the enum value) pub fn for_variant(&self) -> VariantIdent { match self { Self::Fixed(f) => f.for_variant(), @@ -991,17 +1027,7 @@ impl ConceptualRustType { /// can_fail is for cases where checks (e.g. range checks) are done if there /// is a type transformation (i.e. wrapper types) like text (wasm) -> #6.14(text) (rust) pub fn from_wasm_boundary_clone(&self, expr: &str, can_fail: bool) -> Vec { - //assert!(matches!(self, Self::Tagged(_, _)) || !can_fail); - match self { - // Self::Tagged(_tag, ty) => { - // let mut inner = ty.from_wasm_boundary_clone(expr, can_fail); - // if can_fail { - // inner.push(ToWasmBoundaryOperations::TryInto); - // } else { - // inner.push(ToWasmBoundaryOperations::Into); - // } - // inner - // }, + let mut ops = match self { Self::Rust(_ident) => vec![ ToWasmBoundaryOperations::Code(format!("{}.clone()", expr)), ToWasmBoundaryOperations::Into, @@ -1021,22 +1047,16 @@ impl ConceptualRustType { ToWasmBoundaryOperations::Into, ], _ => vec![ToWasmBoundaryOperations::Code(expr.to_owned())], + }; + if can_fail { + ops.push(ToWasmBoundaryOperations::TryInto); } + ops } fn from_wasm_boundary_clone_optional(&self, expr: &str, can_fail: bool) -> Vec { - //assert!(matches!(self, Self::Tagged(_, _)) || !can_fail); - match self { + let mut ops = match self { Self::Primitive(_p) => vec![ToWasmBoundaryOperations::Code(expr.to_owned())], - // Self::Tagged(_tag, ty) => { - // let mut inner = ty.from_wasm_boundary_clone_optional(expr, can_fail); - // if can_fail { - // inner.push(ToWasmBoundaryOperations::TryInto); - // } else { - // inner.push(ToWasmBoundaryOperations::Into); - // } - // inner - // }, Self::Alias(_ident, ty) => ty.from_wasm_boundary_clone_optional(expr, can_fail), Self::Array(..) | Self::Rust(..) | @@ -1049,7 +1069,11 @@ impl ConceptualRustType { }, ], _ => panic!("unsupported or unexpected"), + }; + if can_fail { + ops.push(ToWasmBoundaryOperations::TryInto); } + ops } /// for non-owning parameter TYPES from wasm @@ -1612,7 +1636,24 @@ impl RustRecord { // maps are defined by their keys instead (although they shouldn't have multi-length values either...) Representation::Map => ("_", String::from("1")), }; - conditional_field_expr.push_str(&format!("match &self.{} {{ Some({}) => {}, None => 0 }}", field.name, field_expr, field_contribution)); + if let Some(default_value) = &field.rust_type.default { + if CLI_ARGS.preserve_encodings { + conditional_field_expr.push_str(&format!( + "if self.{} != {} || self.encodings.as_ref().map(|encs| encs.{}_default_present).unwrap_or(false) {{ {} }} else {{ 0 }}", + field.name, + default_value.to_primitive_str_compare(), + field.name, + field_contribution)); + } else { + conditional_field_expr.push_str(&format!( + "if self.{} != {} {{ {} }} else {{ 0 }}", + field.name, + default_value.to_primitive_str_compare(), + field_contribution)); + } + } else { + conditional_field_expr.push_str(&format!("match &self.{} {{ Some({}) => {}, None => 0 }}", field.name, field_expr, field_contribution)); + } } else { match self.rep { Representation::Array => match field.rust_type.conceptual_type.expanded_field_count(types) { diff --git a/src/parsing.rs b/src/parsing.rs index e10fdaf..0e7c9f9 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -32,6 +32,7 @@ use crate::utils::{ enum ControlOperator { Range((Option, Option)), CBOR(RustType), + Default(FixedValue), } struct Type2AndParent<'a> { @@ -143,6 +144,15 @@ fn type2_to_number_literal(type2: &Type2) -> isize { } } +fn type2_to_fixed_value(type2: &Type2) -> FixedValue { + match type2 { + Type2::UintValue{ value, .. } => FixedValue::Uint(*value), + Type2::IntValue{ value, .. } => FixedValue::Nint(*value), + Type2::TextValue{ value, .. } => FixedValue::Text(value.to_string()), + _ => panic!("Type2: {:?} does not correspond to a supported FixedValue", type2), + } +} + fn parse_control_operator(types: &mut IntermediateTypes, parent: &Type2AndParent, operator: &Operator) -> ControlOperator { let lower_bound = match parent.type2 { Type2::Typename{ ident, .. } if ident.to_string() == "uint" => Some(0), @@ -165,10 +175,10 @@ fn parse_control_operator(types: &mut IntermediateTypes, parent: &Type2AndParent ControlOperator::Range((Some(range_start as i128), Some(if is_inclusive { range_end as i128 } else { (range_end + 1) as i128 }))) }, RangeCtlOp::CtlOp{ ctrl, .. } => match ctrl { - cddl::token::ControlOperator::DEFAULT | cddl::token::ControlOperator::CBORSEQ | cddl::token::ControlOperator::WITHIN | cddl::token::ControlOperator::AND => todo!("control operator {} not supported", ctrl), + cddl::token::ControlOperator::DEFAULT => ControlOperator::Default(type2_to_fixed_value(&operator.type2)), cddl::token::ControlOperator::CBOR => ControlOperator::CBOR(rust_type_from_type2(types, &Type2AndParent { type2: &operator.type2, parent: parent.parent, })), cddl::token::ControlOperator::EQ => ControlOperator::Range((Some(type2_to_number_literal(&operator.type2) as i128), Some(type2_to_number_literal(&operator.type2) as i128))), // TODO: this would be MUCH nicer (for error displaying, etc) to handle this in its own dedicated way @@ -286,6 +296,11 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: }, _ => panic!(".cbor is only allowed on bytes as per CDDL spec"), }, + ControlOperator::Default(default_value) => { + let default_type = rust_type_from_type2(types, &Type2AndParent { type2: &type1.type2, parent: &type1 }) + .default(default_value); + types.register_type_alias(type_name.clone(), default_type, true, true); + }, } }, None => { @@ -365,6 +380,12 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice: _ => types.register_rust_struct(RustStruct::new_wrapper(type_name.clone(), *tag, new_type, Some(min_max))) } }, + Some(ControlOperator::Default(default_value)) => { + let default_tagged_type = rust_type_from_type2(types, &Type2AndParent { parent: &inner_type.type1, type2: &inner_type.type1.type2 }) + .default(default_value) + .tag(tag_unwrap); + types.register_type_alias(type_name.clone(), default_tagged_type, true, true); + }, None => { // TODO: this would be fixed if we ordered definitions via a dependency graph to begin with // which would also allow us to do a single pass instead of many like we do now @@ -599,19 +620,24 @@ fn group_entry_to_raw_field_name(entry: &GroupEntry) -> Option { fn rust_type_from_type1(types: &mut IntermediateTypes, type1: &Type1) -> RustType { let control = type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: type1, type2: &type1.type2 }, op)); + let base_type = rust_type_from_type2(types, &Type2AndParent { type2: &type1.type2, parent: type1, }); // println!("type1: {:#?}", type1); match control { - Some(ControlOperator::CBOR(ty)) => ty.as_bytes(), + Some(ControlOperator::CBOR(ty)) => { + assert!(matches!(base_type.conceptual_type.resolve_aliases(), ConceptualRustType::Primitive(Primitive::Bytes))); + ty.as_bytes() + }, Some(ControlOperator::Range(min_max)) => { match &type1.type2 { Type2::Typename{ ident, .. } if ident.to_string() == "uint" || ident.to_string() == "int" => match range_to_primitive(min_max.0, min_max.1) { Some(t) => t.into(), None => panic!("unsupported range for {:?}: {:?}", ident.to_string().as_str(), control) }, - _ => rust_type_from_type2(types, &Type2AndParent { type2: &type1.type2, parent: type1, }) + _ => base_type } }, - _ => rust_type_from_type2(types, &Type2AndParent { type2: &type1.type2, parent: type1, }) + Some(ControlOperator::Default(default_value)) => base_type.default(default_value), + None => base_type, } } diff --git a/tests/core/input.cddl b/tests/core/input.cddl index f2f34dd..d5a100c 100644 --- a/tests/core/input.cddl +++ b/tests/core/input.cddl @@ -61,4 +61,11 @@ signed_ints = [ ; The fix would be ideal as even though the true min in CBOR would be -u64::MAX ; we can't test that since isize::BITS is never > 64 in any normal system and likely never will be i64_min: -9223372036854775808 -] \ No newline at end of file +] + +default_uint = uint .default 1337 + +map_with_defaults = { + ? 1 : default_uint + ? 2 : text .default "two" +} \ No newline at end of file diff --git a/tests/core/tests.rs b/tests/core/tests.rs index 512c9cb..324f4ec 100644 --- a/tests/core/tests.rs +++ b/tests/core/tests.rs @@ -126,4 +126,14 @@ mod tests { let max = SignedInts::new(u8::MAX, u16::MAX, u32::MAX, u64::MAX, i8::MAX, i16::MAX, i32::MAX, i64::MAX, u64::MAX); deser_test(&max); } + + #[test] + fn defaults() { + let mut md = MapWithDefaults::new(); + deser_test(&md); + md.key_1 = 0; + deser_test(&md); + md.key_2 = "not two".into(); + deser_test(&md); + } } diff --git a/tests/preserve-encodings/input.cddl b/tests/preserve-encodings/input.cddl index 70c279e..94696d2 100644 --- a/tests/preserve-encodings/input.cddl +++ b/tests/preserve-encodings/input.cddl @@ -56,4 +56,11 @@ signed_ints = [ ; The fix would be ideal as even though the true min in CBOR would be -u64::MAX ; we can't test that since isize::BITS is never > 64 in any normal system and likely never will be i64_min: -9223372036854775808 -] \ No newline at end of file +] + +default_uint = uint .default 1337 + +map_with_defaults = { + ? 1 : default_uint + ? 2 : text .default "two" +} \ No newline at end of file diff --git a/tests/preserve-encodings/tests.rs b/tests/preserve-encodings/tests.rs index 7ddc9c5..2b86db3 100644 --- a/tests/preserve-encodings/tests.rs +++ b/tests/preserve-encodings/tests.rs @@ -506,4 +506,48 @@ mod tests { assert_eq!(irregular_bytes_max, irregular_max.to_bytes()); } } + + #[test] + fn defaults() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_3_encodings = vec![ + StringLenSz::Len(Sz::Eight), + StringLenSz::Len(Sz::Inline), + StringLenSz::Indefinite(vec![(1, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(2, Sz::Inline), (0, Sz::Inline), (1, Sz::Four)]), + ]; + let bools = [(false, true), (true, false), (true, true)]; + for str_enc in &str_3_encodings { + for def_enc in &def_encodings { + for ((key_1_present, key_1_default), (key_2_present, key_2_default)) in bools.iter().zip(bools.iter()) { + let value_1: u64 = if *key_1_default { 1337 } else { 2 }; + let value_2 = if *key_2_default { "two" } else { "one" }; + let irregular_bytes = vec![ + vec![MAP_INDEF], + if *key_1_present { + vec![ + cbor_int(1, *def_enc), + cbor_int(value_1 as i128, Sz::Two), + ].into_iter().flatten().clone().collect::>() + } else { + vec![] + }, + if *key_2_present { + vec![ + cbor_int(2, *def_enc), + cbor_str_sz(value_2, str_enc.clone()), + ].into_iter().flatten().clone().collect::>() + } else { + vec![] + }, + vec![BREAK], + ].into_iter().flatten().clone().collect::>(); + let irregular = MapWithDefaults::from_bytes(irregular_bytes.clone()).unwrap(); + assert_eq!(irregular_bytes, irregular.to_bytes()); + assert_eq!(irregular.key_1, value_1); + assert_eq!(irregular.key_2, value_2); + } + } + } + } } \ No newline at end of file