diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58b74e71..7c935296 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -163,3 +163,27 @@ jobs: shopt -s globstar tsc bindings/**/*.ts --noEmit --noUnusedLocals rm -rf bindings + + test-windows: + name: Test ts-rs with --all-features on Windows + runs-on: windows-latest + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + steps: + - uses: actions/checkout@v4 + - uses: rui314/setup-mold@v1 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: Swatinem/rust-cache@v2 + - name: Test + working-directory: ts-rs + # Create empty tsconfig and cd into bindings to make tsc + # compile every ts file in the directory + run: | + cargo test --all-features + "{}" | Out-File bindings/tsconfig.json + cd bindings + npm i -g typescript + tsc --noEmit --noUnusedLocals --strict + cd .. + rm -r -fo bindings diff --git a/.gitignore b/.gitignore index 785ee224..f8bdb731 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ target/ /.idea *.ts +tsconfig.json /ts-rs/tests-out ts_rs.meta \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf7614f..ec8db009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,19 @@ # master ### Breaking +- Change how `HashMap` is represented in TypeScript. The resulting bindings (`{ [key in K]?: V }` instead of `{ [key: K]: V }`) are more accurate and flexible. + ### Features -- The `bson-uuid-impl` feature now supports `bson::oid::ObjectId` as well ([#340](https://github.com/Aleph-Alpha/ts-rs/pull/340)) - Allow multile types to have the same `#[ts(export_to = "...")]` attribute and be exported to the same file ([#316](https://github.com/Aleph-Alpha/ts-rs/pull/316)) +- The `bson-uuid-impl` feature now supports `bson::oid::ObjectId` as well ([#340](https://github.com/Aleph-Alpha/ts-rs/pull/340)) +- Add support for types from `smol_str` behind cargo feature `smol_str-impl` ([#350](https://github.com/Aleph-Alpha/ts-rs/pull/350)) +- Support `#[ts(as = "...")]` and `#[ts(type = "...")]` on enum variants ([#384](https://github.com/Aleph-Alpha/ts-rs/pull/384)) ### Fixes - Properly handle block doc comments ([#342](https://github.com/Aleph-Alpha/ts-rs/pull/342)) +- Fix error in internally tagged enums with flattened fields ([#344](https://github.com/Aleph-Alpha/ts-rs/pull/344)) +- Always use forward slash on import paths ([#346](https://github.com/Aleph-Alpha/ts-rs/pull/346)) # 9.0.1 ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 975f059a..c76ab632 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -87,43 +87,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "ast_node" @@ -134,14 +134,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -166,9 +166,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "better_scoped_tls" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de" +checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" dependencies = [ "scoped-tls", ] @@ -205,11 +205,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "bson" -version = "2.11.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a88e82b9106923b5c4d6edfca9e7db958d4e98a478ec115022e81b9b38e2c8" +checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" dependencies = [ "ahash", "base64", @@ -243,9 +252,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cargo-ts" @@ -259,9 +268,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.6" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -269,6 +281,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -286,9 +304,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.10" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -296,9 +314,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.10" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -308,21 +326,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "color-eyre" @@ -353,15 +371,15 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-url" @@ -430,7 +448,7 @@ checksum = "f3ab0dd2bedc109d25f0d21afb09b7d329f6c6fa83b095daf31d2d967e091548" dependencies = [ "anyhow", "bumpalo", - "hashbrown", + "hashbrown 0.14.5", "indexmap", "rustc-hash", "serde", @@ -528,7 +546,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -573,6 +591,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heapless" version = "0.8.0" @@ -597,11 +621,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hstr" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96274be293b8877e61974a607105d09c84caebe9620b47774aa8a6b942042dd4" +checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", "phf", @@ -611,9 +635,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -650,32 +674,32 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", "serde", ] [[package]] name = "is-macro" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a85abdc13717906baccb5a1e435556ce0df215f242892f721dff62bf25288f" +checksum = "2069faacbe981460232f880d26bf3c7634e322d49053aa48c27e3ae642f728f1" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" @@ -685,9 +709,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b" dependencies = [ "wasm-bindgen", ] @@ -700,9 +724,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libm" @@ -783,15 +807,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "ordered-float" -version = "4.2.1" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ff2cf528c6c03d9ed653d6c4ce1dc0582dc4af309790ad92f07c1cd551b0be" +checksum = "44d501f1a72f71d3c063a6bbc8f7271fa73aa09fe5d6283b6571e2ed176a2537" dependencies = [ "num-traits", ] @@ -838,7 +862,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -864,33 +888,36 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -933,9 +960,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -945,9 +972,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -956,9 +983,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" @@ -992,9 +1019,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1010,23 +1037,24 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap", "itoa", + "memchr", "ryu", "serde", ] @@ -1049,6 +1077,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "siphasher" version = "0.3.11" @@ -1072,6 +1106,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "smol_str" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66eaf762c5af19db3108300515c8aa7a50efc90ff745f4c62288052ebf9fdd25" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1080,15 +1124,15 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -1106,7 +1150,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -1200,18 +1244,18 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] name = "swc_macros_common" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +checksum = "27e18fbfe83811ffae2bb23727e45829a0d19c6870bced7c0f545cc99ad248dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -1234,7 +1278,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -1250,9 +1294,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1285,22 +1329,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -1393,6 +1437,16 @@ dependencies = [ "winnow", ] +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "pin-project-lite", +] + [[package]] name = "tracing" version = "0.1.40" @@ -1412,7 +1466,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] [[package]] @@ -1448,9 +1502,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" dependencies = [ "serde", "stable_deref_trait", @@ -1458,7 +1512,7 @@ dependencies = [ [[package]] name = "ts-rs" -version = "9.0.1" +version = "10.0.0" dependencies = [ "bigdecimal", "bson", @@ -1472,7 +1526,9 @@ dependencies = [ "semver", "serde", "serde_json", + "smol_str", "thiserror", + "tokio", "ts-rs-macros", "url", "uuid", @@ -1480,11 +1536,11 @@ dependencies = [ [[package]] name = "ts-rs-macros" -version = "9.0.1" +version = "10.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", "termcolor", ] @@ -1496,9 +1552,9 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-id-start" @@ -1508,24 +1564,24 @@ checksum = "02aebfa694eccbbbffdd92922c7de136b9fe764396d2f10e21bce1681477cfc1" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "url" @@ -1563,9 +1619,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1575,34 +1631,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1610,54 +1667,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.52.0" @@ -1676,6 +1711,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1755,6 +1799,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -1766,5 +1811,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.79", ] diff --git a/README.md b/README.md index 27fae724..01deb91f 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ We recommend doing this in your tests. ### Get started ```toml [dependencies] -ts-rs = "9.0" +ts-rs = "10.0" ``` ```rust @@ -86,6 +86,8 @@ When running `cargo test`, the TypeScript bindings will be exported to the file | ordered-float-impl | Implement `TS` for types from *ordered_float* | | heapless-impl | Implement `TS` for types from *heapless* | | semver-impl | Implement `TS` for types from *semver* | +| smol_str-impl | Implement `TS` for types from *smol_str* | +| tokio-impl | Implement `TS` for types from *tokio* |
diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 98fa642a..ee41ec30 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ts-rs-macros" -version = "9.0.1" +version = "10.0.0" authors = ["Moritz Bischof "] edition = "2021" description = "derive macro for ts-rs" diff --git a/macros/src/attr/variant.rs b/macros/src/attr/variant.rs index cd1fee9d..51d1da4e 100644 --- a/macros/src/attr/variant.rs +++ b/macros/src/attr/variant.rs @@ -1,13 +1,15 @@ -use syn::{Attribute, Fields, Ident, Result, Variant}; +use syn::{Attribute, Fields, Ident, Result, Type, Variant}; use super::{Attr, Serde}; use crate::{ - attr::{parse_assign_inflection, parse_assign_str, Inflection}, + attr::{parse_assign_from_str, parse_assign_inflection, parse_assign_str, Inflection}, utils::parse_attrs, }; #[derive(Default)] pub struct VariantAttr { + pub type_as: Option, + pub type_override: Option, pub rename: Option, pub rename_all: Option, pub inline: bool, @@ -31,6 +33,8 @@ impl Attr for VariantAttr { fn merge(self, other: Self) -> Self { Self { + type_as: self.type_as.or(other.type_as), + type_override: self.type_override.or(other.type_override), rename: self.rename.or(other.rename), rename_all: self.rename_all.or(other.rename_all), inline: self.inline || other.inline, @@ -40,6 +44,38 @@ impl Attr for VariantAttr { } fn assert_validity(&self, item: &Self::Item) -> Result<()> { + if self.type_as.is_some() { + if self.type_override.is_some() { + syn_err_spanned!( + item; + "`as` is not compatible with `type`" + ) + } + + if self.rename_all.is_some() { + syn_err_spanned!( + item; + "`as` is not compatible with `rename_all`" + ) + } + } + + if self.type_override.is_some() { + if self.rename_all.is_some() { + syn_err_spanned!( + item; + "`type` is not compatible with `rename_all`" + ) + } + + if self.inline { + syn_err_spanned!( + item; + "`type` is not compatible with `inline`" + ) + } + } + if !matches!(item.fields, Fields::Named(_)) && self.rename_all.is_some() { syn_err_spanned!( item; @@ -53,6 +89,8 @@ impl Attr for VariantAttr { impl_parse! { VariantAttr(input, out) { + "as" => out.type_as = Some(parse_assign_from_str(input)?), + "type" => out.type_override = Some(parse_assign_str(input)?), "rename" => out.rename = Some(parse_assign_str(input)?), "rename_all" => out.rename_all = Some(parse_assign_inflection(input)?), "inline" => out.inline = true, diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index 009741e7..95cd38d9 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -1,210 +1,210 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::{Fields, ItemEnum, Variant}; - -use crate::{ - attr::{Attr, EnumAttr, FieldAttr, StructAttr, Tagged, VariantAttr}, - deps::Dependencies, - types::{self, type_as, type_override}, - DerivedTS, -}; - -pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { - let enum_attr: EnumAttr = EnumAttr::from_attrs(&s.attrs)?; - - enum_attr.assert_validity(s)?; - - let crate_rename = enum_attr.crate_rename(); - - let name = match &enum_attr.rename { - Some(existing) => existing.clone(), - None => s.ident.to_string(), - }; - - if let Some(attr_type_override) = &enum_attr.type_override { - return type_override::type_override_enum(&enum_attr, &name, attr_type_override); - } - if let Some(attr_type_as) = &enum_attr.type_as { - return type_as::type_as_enum(&enum_attr, &name, attr_type_as); - } - - if s.variants.is_empty() { - return Ok(empty_enum(name, enum_attr)); - } - - if s.variants.is_empty() { - return Ok(DerivedTS { - crate_rename: crate_rename.clone(), - ts_name: name, - docs: enum_attr.docs, - inline: quote!("never".to_owned()), - inline_flattened: None, - dependencies: Dependencies::new(crate_rename), - export: enum_attr.export, - export_to: enum_attr.export_to, - concrete: enum_attr.concrete, - bound: enum_attr.bound, - }); - } - - let mut formatted_variants = Vec::new(); - let mut dependencies = Dependencies::new(crate_rename.clone()); - for variant in &s.variants { - format_variant( - &mut formatted_variants, - &mut dependencies, - &enum_attr, - variant, - )?; - } - - Ok(DerivedTS { - crate_rename, - inline: quote!([#(#formatted_variants),*].join(" | ")), - inline_flattened: Some(quote!( - format!("({})", [#(#formatted_variants),*].join(" | ")) - )), - dependencies, - docs: enum_attr.docs, - export: enum_attr.export, - export_to: enum_attr.export_to, - ts_name: name, - concrete: enum_attr.concrete, - bound: enum_attr.bound, - }) -} - -fn format_variant( - formatted_variants: &mut Vec, - dependencies: &mut Dependencies, - enum_attr: &EnumAttr, - variant: &Variant, -) -> syn::Result<()> { - let crate_rename = enum_attr.crate_rename(); - - // If `variant.fields` is not a `Fields::Named(_)` the `rename_all_fields` - // attribute must be ignored to prevent a `rename_all` from getting to - // the newtype, tuple or unit formatting, which would cause an error - let variant_attr = VariantAttr::from_attrs(&variant.attrs)?; - - variant_attr.assert_validity(variant)?; - - if variant_attr.skip { - return Ok(()); - } - - let untagged_variant = variant_attr.untagged; - let name = match (variant_attr.rename.clone(), &enum_attr.rename_all) { - (Some(rn), _) => rn, - (None, None) => variant.ident.to_string(), - (None, Some(rn)) => rn.apply(&variant.ident.to_string()), - }; - - let struct_attr = StructAttr::from_variant(enum_attr, &variant_attr, &variant.fields); - let variant_type = types::type_def( - &struct_attr, - // In internally tagged enums, we can tag the struct - &name, - &variant.fields, - )?; - let variant_dependencies = variant_type.dependencies; - let inline_type = variant_type.inline; - - let formatted = match (untagged_variant, enum_attr.tagged()?) { - (true, _) | (_, Tagged::Untagged) => quote!(#inline_type), - (false, Tagged::Externally) => match &variant.fields { - Fields::Unit => quote!(format!("\"{}\"", #name)), - Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { - let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&field.attrs)?; - - field_attr.assert_validity(field)?; - - if field_attr.skip { - quote!(format!("\"{}\"", #name)) - } else { - quote!(format!("{{ \"{}\": {} }}", #name, #inline_type)) - } - } - _ => quote!(format!("{{ \"{}\": {} }}", #name, #inline_type)), - }, - (false, Tagged::Adjacently { tag, content }) => match &variant.fields { - Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { - let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; - - field_attr.assert_validity(field)?; - - if field_attr.skip { - quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)) - } else { - let ty = match field_attr.type_override { - Some(type_override) => quote!(#type_override), - None => { - let ty = field_attr.type_as(&field.ty); - quote!(<#ty as #crate_rename::TS>::name()) - } - }; - quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #name, #content, #ty)) - } - } - Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)), - _ => quote!( - format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #name, #content, #inline_type) - ), - }, - (false, Tagged::Internally { tag }) => match variant_type.inline_flattened { - Some(_) => { - quote! { #inline_type } - } - None => match &variant.fields { - Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { - let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; - - field_attr.assert_validity(field)?; - - if field_attr.skip { - quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)) - } else { - let ty = match field_attr.type_override { - Some(type_override) => quote! { #type_override }, - None => { - let ty = field_attr.type_as(&field.ty); - quote!(<#ty as #crate_rename::TS>::name()) - } - }; - - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #name, #ty)) - } - } - Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)), - _ => { - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #name, #inline_type)) - } - }, - }, - }; - - dependencies.append(variant_dependencies); - formatted_variants.push(formatted); - Ok(()) -} - -// bindings for an empty enum (`never` in TS) -fn empty_enum(name: impl Into, enum_attr: EnumAttr) -> DerivedTS { - let name = name.into(); - let crate_rename = enum_attr.crate_rename(); - DerivedTS { - crate_rename: crate_rename.clone(), - inline: quote!("never".to_owned()), - docs: enum_attr.docs, - inline_flattened: None, - dependencies: Dependencies::new(crate_rename), - export: enum_attr.export, - export_to: enum_attr.export_to, - ts_name: name, - concrete: enum_attr.concrete, - bound: enum_attr.bound, - } -} +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Fields, ItemEnum, Variant}; + +use crate::{ + attr::{Attr, EnumAttr, FieldAttr, StructAttr, Tagged, VariantAttr}, + deps::Dependencies, + types::{self, type_as, type_override}, + DerivedTS, +}; + +pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { + let enum_attr: EnumAttr = EnumAttr::from_attrs(&s.attrs)?; + + enum_attr.assert_validity(s)?; + + let crate_rename = enum_attr.crate_rename(); + + let name = match &enum_attr.rename { + Some(existing) => existing.clone(), + None => s.ident.to_string(), + }; + + if let Some(attr_type_override) = &enum_attr.type_override { + return type_override::type_override_enum(&enum_attr, &name, attr_type_override); + } + + if let Some(attr_type_as) = &enum_attr.type_as { + return type_as::type_as_enum(&enum_attr, &name, attr_type_as); + } + + if s.variants.is_empty() { + return Ok(empty_enum(name, enum_attr)); + } + + let mut formatted_variants = Vec::new(); + let mut dependencies = Dependencies::new(crate_rename.clone()); + + for variant in &s.variants { + format_variant( + &mut formatted_variants, + &mut dependencies, + &enum_attr, + variant, + )?; + } + + Ok(DerivedTS { + crate_rename, + inline: quote!([#(#formatted_variants),*].join(" | ")), + inline_flattened: Some(quote!( + format!("({})", [#(#formatted_variants),*].join(" | ")) + )), + dependencies, + docs: enum_attr.docs, + export: enum_attr.export, + export_to: enum_attr.export_to, + ts_name: name, + concrete: enum_attr.concrete, + bound: enum_attr.bound, + }) +} + +fn format_variant( + formatted_variants: &mut Vec, + dependencies: &mut Dependencies, + enum_attr: &EnumAttr, + variant: &Variant, +) -> syn::Result<()> { + let crate_rename = enum_attr.crate_rename(); + + // If `variant.fields` is not a `Fields::Named(_)` the `rename_all_fields` + // attribute must be ignored to prevent a `rename_all` from getting to + // the newtype, tuple or unit formatting, which would cause an error + let variant_attr = VariantAttr::from_attrs(&variant.attrs)?; + + variant_attr.assert_validity(variant)?; + + if variant_attr.skip { + return Ok(()); + } + + let untagged_variant = variant_attr.untagged; + let name = match (variant_attr.rename.clone(), &enum_attr.rename_all) { + (Some(rn), _) => rn, + (None, None) => variant.ident.to_string(), + (None, Some(rn)) => rn.apply(&variant.ident.to_string()), + }; + + let struct_attr = StructAttr::from_variant(enum_attr, &variant_attr, &variant.fields); + let variant_type = types::type_def( + &struct_attr, + // In internally tagged enums, we can tag the struct + &name, + &variant.fields, + )?; + + let variant_dependencies = variant_type.dependencies; + let inline_type = variant_type.inline; + + let parsed_ty = match (&variant_attr.type_as, &variant_attr.type_override) { + (Some(_), Some(_)) => syn_err_spanned!(variant; "`type` is not compatible with `as`"), + (Some(ty), None) => { + dependencies.push(ty); + quote!(<#ty as #crate_rename::TS>::name()) + } + (None, Some(ty)) => quote!(#ty.to_owned()), + (None, None) => { + dependencies.append(variant_dependencies); + inline_type + } + }; + + let formatted = match (untagged_variant, enum_attr.tagged()?) { + (true, _) | (_, Tagged::Untagged) => quote!(#parsed_ty), + (false, Tagged::Externally) => match &variant.fields { + Fields::Unit => quote!(format!("\"{}\"", #name)), + Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { + let field = &unnamed.unnamed[0]; + let field_attr = FieldAttr::from_attrs(&field.attrs)?; + + field_attr.assert_validity(field)?; + + if field_attr.skip { + quote!(format!("\"{}\"", #name)) + } else { + quote!(format!("{{ \"{}\": {} }}", #name, #parsed_ty)) + } + } + _ => quote!(format!("{{ \"{}\": {} }}", #name, #parsed_ty)), + }, + (false, Tagged::Adjacently { tag, content }) => match &variant.fields { + Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { + let field = &unnamed.unnamed[0]; + let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + + field_attr.assert_validity(field)?; + + if field_attr.skip { + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)) + } else { + let ty = match field_attr.type_override { + Some(type_override) => quote!(#type_override), + None => { + let ty = field_attr.type_as(&field.ty); + quote!(<#ty as #crate_rename::TS>::name()) + } + }; + quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #name, #content, #ty)) + } + } + Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)), + _ => quote!( + format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #name, #content, #parsed_ty) + ), + }, + (false, Tagged::Internally { tag }) => match variant_type.inline_flattened { + Some(_) => { + quote! { #parsed_ty } + } + None => match &variant.fields { + Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { + let field = &unnamed.unnamed[0]; + let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + + field_attr.assert_validity(field)?; + + if field_attr.skip { + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)) + } else { + let ty = match field_attr.type_override { + Some(type_override) => quote! { #type_override }, + None => { + let ty = field_attr.type_as(&field.ty); + quote!(<#ty as #crate_rename::TS>::name()) + } + }; + + quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #name, #ty)) + } + } + Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)), + _ => { + quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #name, #parsed_ty)) + } + }, + }, + }; + + formatted_variants.push(formatted); + Ok(()) +} + +// bindings for an empty enum (`never` in TS) +fn empty_enum(name: impl Into, enum_attr: EnumAttr) -> DerivedTS { + let name = name.into(); + let crate_rename = enum_attr.crate_rename(); + DerivedTS { + crate_rename: crate_rename.clone(), + inline: quote!("never".to_owned()), + docs: enum_attr.docs, + inline_flattened: None, + dependencies: Dependencies::new(crate_rename), + export: enum_attr.export, + export_to: enum_attr.export_to, + ts_name: name, + concrete: enum_attr.concrete, + bound: enum_attr.bound, + } +} diff --git a/ts-rs/Cargo.toml b/ts-rs/Cargo.toml index 5c24c463..abbdceb2 100644 --- a/ts-rs/Cargo.toml +++ b/ts-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ts-rs" -version = "9.0.1" +version = "10.0.0" authors = ["Moritz Bischof "] edition = "2021" license = "MIT" @@ -31,20 +31,23 @@ indexmap-impl = ["indexmap"] ordered-float-impl = ["ordered-float"] heapless-impl = ["heapless"] semver-impl = ["semver"] +smol_str-impl = ["smol_str"] serde-json-impl = ["serde_json"] no-serde-warnings = ["ts-rs-macros/no-serde-warnings"] import-esm = [] export = ["ts-rs-macros/export"] generate-metadata = [] +tokio-impl = ["tokio"] [dev-dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1" chrono = { version = "0.4", features = ["serde"] } +tokio = { version = "1.40", features = ["sync", "rt"] } [dependencies] heapless = { version = ">= 0.7, < 0.9", optional = true } -ts-rs-macros = { version = "=9.0.1", path = "../macros" } +ts-rs-macros = { version = "=10.0.0", path = "../macros" } dprint-plugin-typescript = { version = "0.90", optional = true } chrono = { version = "0.4", optional = true } bigdecimal = { version = ">= 0.0.13, < 0.5", features = [ @@ -55,9 +58,10 @@ bson = { version = "2", optional = true } bytes = { version = "1", optional = true } url = { version = "2", optional = true } semver = { version = "1", optional = true } +smol_str = { version = "0.3", optional = true } thiserror = "1" indexmap = { version = "2", optional = true } ordered-float = { version = ">= 3, < 5", optional = true } serde_json = { version = "1", optional = true } lazy_static = { version = "1", default-features = false } - +tokio = { version = "1", features = ["sync"], optional = true } diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index 420b8c54..a69a3b92 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -341,7 +341,7 @@ fn generate_imports( writeln!( out, - "import type {{ {} }} from {:?};", + r#"import type {{ {} }} from "{}";"#, &dep.ts_name, rel_path )?; } @@ -352,11 +352,19 @@ fn generate_imports( /// Returns the required import path for importing `import` from the file `from` fn import_path(from: &Path, import: &Path) -> Result { let rel_path = diff_paths(import, from.parent().unwrap())?; - let path = match rel_path.components().next() { - Some(Component::Normal(_)) => format!("./{}", rel_path.to_string_lossy()), + let str_path = match rel_path.components().next() { + Some(Component::Normal(_)) => { + format!("./{}", rel_path.to_string_lossy()) + } _ => rel_path.to_string_lossy().into(), }; + let path = if cfg!(target_os = "windows") { + str_path.replace('\\', "/") + } else { + str_path + }; + let path_without_extension = path.trim_end_matches(".ts"); Ok(if cfg!(feature = "import-esm") { diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 76e67290..2228d9dc 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -40,7 +40,7 @@ //! ## Get started //! ```toml //! [dependencies] -//! ts-rs = "9.0" +//! ts-rs = "10.0" //! ``` //! //! ```rust @@ -84,6 +84,8 @@ //! | ordered-float-impl | Implement `TS` for types from *ordered_float* | //! | heapless-impl | Implement `TS` for types from *heapless* | //! | semver-impl | Implement `TS` for types from *semver* | +//! | smol_str-impl | Implement `TS` for types from *smol_str* | +//! | tokio-impl | Implement `TS` for types from *tokio* | //! //!
//! @@ -138,6 +140,8 @@ mod chrono; mod export; #[cfg(feature = "serde-json-impl")] mod serde_json; +#[cfg(feature = "tokio-impl")] +mod tokio; /// A type which can be represented in TypeScript. /// Most of the time, you'd want to derive this trait instead of implementing it manually. @@ -1005,6 +1009,7 @@ impl_wrapper!(impl<'a, T: TS + ToOwned + ?Sized> TS for std::borrow::Cow<'a, T>) impl_wrapper!(impl TS for std::cell::Cell); impl_wrapper!(impl TS for std::cell::RefCell); impl_wrapper!(impl TS for std::sync::Mutex); +impl_wrapper!(impl TS for std::sync::RwLock); impl_wrapper!(impl TS for std::sync::Weak); impl_wrapper!(impl TS for std::marker::PhantomData); @@ -1013,6 +1018,9 @@ impl_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); #[cfg(feature = "bigdecimal-impl")] impl_primitives! { bigdecimal::BigDecimal => "string" } +#[cfg(feature = "smol_str-impl")] +impl_primitives! { smol_str::SmolStr => "string" } + #[cfg(feature = "uuid-impl")] impl_primitives! { uuid::Uuid => "string" } @@ -1068,6 +1076,8 @@ impl_primitives! { pub(crate) use impl_primitives; #[rustfmt::skip] pub(crate) use impl_shadow; +#[rustfmt::skip] +pub(crate) use impl_wrapper; #[doc(hidden)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] diff --git a/ts-rs/src/serde_json.rs b/ts-rs/src/serde_json.rs index a5e63ed1..0f9df431 100644 --- a/ts-rs/src/serde_json.rs +++ b/ts-rs/src/serde_json.rs @@ -12,8 +12,10 @@ use super::{impl_primitives, impl_shadow, TS}; pub enum TsJsonValue { Number(i32), String(String), + Boolean(bool), Array(Vec), Object(HashMap), + Null(()), } impl_shadow!(as TsJsonValue: impl TS for serde_json::Value); diff --git a/ts-rs/src/tokio.rs b/ts-rs/src/tokio.rs new file mode 100644 index 00000000..0d90e0a2 --- /dev/null +++ b/ts-rs/src/tokio.rs @@ -0,0 +1,7 @@ +use tokio::sync::{Mutex, OnceCell, RwLock}; + +use super::{impl_wrapper, TypeVisitor, TS}; + +impl_wrapper!(impl TS for Mutex); +impl_wrapper!(impl TS for OnceCell); +impl_wrapper!(impl TS for RwLock); diff --git a/ts-rs/tests/integration/impl_primitive.rs b/ts-rs/tests/integration/impl_primitive.rs new file mode 100644 index 00000000..d30708b1 --- /dev/null +++ b/ts-rs/tests/integration/impl_primitive.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "bigdecimal-impl")] +#[test] +fn impl_primitive_bigdecimal() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} + +#[cfg(feature = "smol_str-impl")] +#[test] +fn impl_primitive_smolstr() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} + +#[cfg(feature = "uuid-impl")] +#[test] +fn impl_primitive_uuid() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} + +#[cfg(feature = "url-impl")] +#[test] +fn impl_primitive_url() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} + +#[cfg(feature = "ordered-float-impl")] +#[test] +fn impl_primitive_order_float() { + assert_eq!( + as ts_rs::TS>::name(), + ::name() + ); + assert_eq!( + as ts_rs::TS>::inline(), + ::inline() + ); + assert_eq!( + as ts_rs::TS>::name(), + ::name() + ); + assert_eq!( + as ts_rs::TS>::inline(), + ::inline() + ) +} + +#[cfg(feature = "bson-uuid-impl")] +#[test] +fn impl_primitive_bson_uuid() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ); + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} + +#[cfg(feature = "semver-impl")] +#[test] +fn impl_primitive_semver() { + assert_eq!( + ::name(), + ::name() + ); + assert_eq!( + ::inline(), + ::inline() + ) +} diff --git a/ts-rs/tests/integration/main.rs b/ts-rs/tests/integration/main.rs index e1ce0e0c..662df57d 100644 --- a/ts-rs/tests/integration/main.rs +++ b/ts-rs/tests/integration/main.rs @@ -20,6 +20,7 @@ mod generics; mod generics_flatten; mod hashmap; mod hashset; +mod impl_primitive; mod imports; mod indexmap; mod infer_as; @@ -51,6 +52,7 @@ mod skip; mod slices; mod struct_rename; mod struct_tag; +mod tokio; mod top_level_type_as; mod top_level_type_override; mod tuple; diff --git a/ts-rs/tests/integration/serde_json.rs b/ts-rs/tests/integration/serde_json.rs index fc0a113b..6c68b8ae 100644 --- a/ts-rs/tests/integration/serde_json.rs +++ b/ts-rs/tests/integration/serde_json.rs @@ -24,7 +24,7 @@ fn using_serde_json() { ); assert_eq!( serde_json::Value::decl(), - "type JsonValue = number | string | Array | { [key in string]?: JsonValue };", + "type JsonValue = number | string | boolean | Array | { [key in string]?: JsonValue } | null;", ); assert_eq!( @@ -53,7 +53,7 @@ fn inlined_value() { assert_eq!( InlinedValue::decl(), "type InlinedValue = { \ - any: number | string | Array | { [key in string]?: JsonValue }, \ + any: number | string | boolean | Array | { [key in string]?: JsonValue } | null, \ };" ); } diff --git a/ts-rs/tests/integration/tokio.rs b/ts-rs/tests/integration/tokio.rs new file mode 100644 index 00000000..ac31371f --- /dev/null +++ b/ts-rs/tests/integration/tokio.rs @@ -0,0 +1,21 @@ +#![cfg(feature = "tokio-impl")] + +use tokio::sync::{Mutex, OnceCell, RwLock}; +use ts_rs::TS; + +#[derive(TS)] +#[ts(export, export_to = "tokio/")] +#[ts(concrete(T = i32))] +struct Tokio { + mutex: Mutex, + once_cell: OnceCell, + rw_lock: RwLock, +} + +#[test] +fn tokio() { + assert_eq!( + Tokio::::decl(), + "type Tokio = { mutex: number, once_cell: number, rw_lock: number, };" + ) +} diff --git a/ts-rs/tests/integration/type_as.rs b/ts-rs/tests/integration/type_as.rs index 4b55bb14..dd84f7df 100644 --- a/ts-rs/tests/integration/type_as.rs +++ b/ts-rs/tests/integration/type_as.rs @@ -4,6 +4,7 @@ use std::{ cell::UnsafeCell, mem::MaybeUninit, ptr::NonNull, sync::atomic::AtomicPtr, time::Instant, }; +use serde::Serialize; use ts_rs::TS; type Unsupported = UnsafeCell>>>; @@ -58,12 +59,51 @@ enum OverrideEnum { }, } +mod deser { + use serde::{Serialize, Serializer}; + + use super::Instant; + pub fn serialize(field: &Instant, serializer: S) -> Result { + #[derive(Serialize)] + struct Foo { + x: i32, + } + Foo { x: 0 }.serialize(serializer) + } +} + +#[derive(TS)] +struct OverrideVariantDef { + x: i32, +} + +#[derive(TS, Serialize)] +#[ts(export, export_to = "type_as/")] +enum OverrideVariant { + #[ts(as = "OverrideVariantDef")] + #[serde(with = "deser")] + A { + x: Instant, + }, + B { + y: i32, + z: i32, + }, +} + #[test] fn enum_variants() { + let a = OverrideVariant::A { x: Instant::now() }; + assert_eq!(serde_json::to_string(&a).unwrap(), r#"{"A":{"x":0}}"#); assert_eq!( OverrideEnum::inline(), r#"{ "A": ExternalTypeDef } | { "B": { x: ExternalTypeDef, y: number, z: number, } }"# - ) + ); + + assert_eq!( + OverrideVariant::inline(), + r#"{ "A": OverrideVariantDef } | { "B": { y: number, z: number, } }"# + ); } #[derive(TS)]