diff --git a/.gitignore b/.gitignore index 9a891bd..db4a567 100644 --- a/.gitignore +++ b/.gitignore @@ -72,7 +72,6 @@ docs/_build/ .python-version # flatbuffers -src/generated +src/planus_flat.rs src/python *.pyi -classes.txt diff --git a/Cargo.lock b/Cargo.lock index bb8d233..b66bd3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,57 +3,76 @@ version = 4 [[package]] -name = "addr2line" -version = "0.24.2" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "gimli", + "memchr", ] [[package]] -name = "adler2" -version = "2.0.1" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] -name = "arbitrary" -version = "1.4.1" +name = "array-init-cursor" +version = "0.2.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" + +[[package]] +name = "ascii-canvas" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" dependencies = [ - "derive_arbitrary", + "term", ] [[package]] -name = "attribute-derive" -version = "0.10.3" +name = "askama" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" dependencies = [ - "attribute-derive-macro", - "derive-where", - "manyhow", + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", "proc-macro2", "quote", + "serde", "syn", ] [[package]] -name = "attribute-derive-macro" +name = "askama_escape" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" dependencies = [ - "collection_literals", - "interpolator", - "manyhow", - "proc-macro-utils", - "proc-macro2", - "quote", - "quote-use", - "syn", + "nom", ] [[package]] @@ -63,51 +82,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "backtrace" -version = "0.3.75" +name = "basic-toml" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", + "serde", ] [[package]] -name = "base64" -version = "0.22.1" +name = "beef" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] -name = "bitflags" -version = "2.9.1" +name = "bit-set" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] [[package]] -name = "bumpalo" -version = "3.19.0" +name = "bit-vec" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] -name = "bytes" -version = "1.10.1" +name = "bitflags" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] -name = "cc" -version = "1.2.27" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "shlex", + "generic-array", ] [[package]] @@ -117,180 +133,140 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "collection_literals" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" - -[[package]] -name = "crc32fast" -version = "1.4.2" +name = "codespan" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" dependencies = [ - "cfg-if", + "codespan-reporting", ] [[package]] -name = "derive-where" -version = "1.5.0" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "termcolor", + "unicode-width", ] [[package]] -name = "derive_arbitrary" -version = "1.4.1" +name = "convert_case" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" dependencies = [ - "proc-macro2", - "quote", - "syn", + "unicode-segmentation", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ - "proc-macro2", - "quote", - "syn", + "libc", ] [[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "flatbuffers" -version = "25.2.10" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "bitflags", - "rustc_version", + "generic-array", + "typenum", ] [[package]] -name = "flate2" -version = "1.1.2" +name = "derive_more" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", + "derive_more-impl", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "derive_more-impl" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "percent-encoding", + "convert_case", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "futures-channel" -version = "0.3.31" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "futures-core", - "futures-sink", + "block-buffer", + "crypto-common", ] [[package]] -name = "futures-core" -version = "0.3.31" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "futures-io" -version = "0.3.31" +name = "ena" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] [[package]] -name = "futures-sink" -version = "0.3.31" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "futures-task" -version = "0.3.31" +name = "eyre" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] [[package]] -name = "futures-util" -version = "0.3.31" +name = "fixedbitset" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] -name = "get-size" -version = "0.2.0" -source = "git+https://github.com/VirxEC/get-size?branch=update#0c14ad3f6de292f11d832ad140434f045c6cfd3c" -dependencies = [ - "get-size-derive", -] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "get-size-derive" -version = "0.2.0" -source = "git+https://github.com/VirxEC/get-size?branch=update#0c14ad3f6de292f11d832ad140434f045c6cfd3c" -dependencies = [ - "attribute-derive", - "quote", - "syn", -] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "getrandom" -version = "0.2.16" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", + "typenum", + "version_check", ] [[package]] @@ -300,24 +276,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", - "wasm-bindgen", + "wasi", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "heck" @@ -326,389 +299,346 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" +name = "home" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "bytes", - "http", + "windows-sys", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "humansize" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", + "libm", ] [[package]] -name = "httparse" -version = "1.10.1" +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] -name = "hyper" -version = "1.6.0" +name = "indexmap" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", + "equivalent", + "hashbrown", ] [[package]] -name = "hyper-rustls" -version = "0.27.7" +name = "indoc" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] -name = "hyper-util" -version = "0.1.14" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", ] [[package]] -name = "icu_collections" -version = "2.0.0" +name = "keccak" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", + "cpufeatures", ] [[package]] -name = "icu_locale_core" -version = "2.0.0" +name = "lalrpop" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", ] [[package]] -name = "icu_normalizer" -version = "2.0.0" +name = "lalrpop-util" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", + "regex-automata", + "rustversion", ] [[package]] -name = "icu_normalizer_data" -version = "2.0.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "icu_properties" -version = "2.0.1" +name = "libc" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "potential_utf", - "zerotrie", - "zerovec", -] +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] -name = "icu_properties_data" -version = "2.0.1" +name = "libm" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] -name = "icu_provider" -version = "2.0.0" +name = "lock_api" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", + "autocfg", + "scopeguard", ] [[package]] -name = "idna" -version = "1.0.3" +name = "log" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "idna_adapter" -version = "1.2.1" +name = "logos" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "ab6f536c1af4c7cc81edf73da1f8029896e7e1e16a219ef09b184e76a296f3db" dependencies = [ - "icu_normalizer", - "icu_properties", + "logos-derive", ] [[package]] -name = "indexmap" -version = "2.9.0" +name = "logos-codegen" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "189bbfd0b61330abea797e5e9276408f2edbe4f822d7ad08685d67419aafb34e" dependencies = [ - "equivalent", - "hashbrown", + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "rustc_version", + "syn", ] [[package]] -name = "indoc" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" - -[[package]] -name = "interpolator" -version = "0.5.0" +name = "logos-derive" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" +checksum = "ebfe8e1a19049ddbfccbd14ac834b215e11b85b90bab0c2dba7c7b92fb5d5cba" +dependencies = [ + "logos-codegen", +] [[package]] -name = "ipnet" -version = "2.11.0" +name = "memchr" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] -name = "iri-string" -version = "0.7.8" +name = "memoffset" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "memchr", - "serde", + "autocfg", ] [[package]] -name = "itoa" -version = "1.0.15" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "js-sys" -version = "0.3.77" +name = "mime_guess" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ - "once_cell", - "wasm-bindgen", + "mime", + "unicase", ] [[package]] -name = "libc" -version = "0.2.174" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "libz-rs-sys" -version = "0.5.1" +name = "new_debug_unreachable" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" -dependencies = [ - "zlib-rs", -] +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] -name = "litemap" -version = "0.8.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] [[package]] -name = "log" -version = "0.4.27" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] [[package]] -name = "lru-slab" -version = "0.1.2" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "manyhow" -version = "0.11.4" +name = "parking_lot" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ - "manyhow-macros", - "proc-macro2", - "quote", - "syn", + "lock_api", + "parking_lot_core", ] [[package]] -name = "manyhow-macros" -version = "0.11.4" +name = "parking_lot_core" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "proc-macro-utils", - "proc-macro2", - "quote", + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", ] [[package]] -name = "memchr" -version = "2.7.5" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "memoffset" -version = "0.9.1" +name = "petgraph" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "autocfg", + "fixedbitset", + "indexmap", ] [[package]] -name = "miniz_oxide" -version = "0.8.9" +name = "phf_shared" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "adler2", + "siphasher", ] [[package]] -name = "mio" -version = "1.0.4" +name = "pico-args" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +name = "planus" +version = "1.1.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" dependencies = [ - "memchr", + "array-init-cursor", + "hashbrown", ] [[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +name = "planus-codegen" +version = "1.1.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" +dependencies = [ + "askama", + "eyre", + "heck", + "planus-types", + "random_color", + "thiserror", + "vec_map", +] [[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +name = "planus-lexer" +version = "1.1.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" +dependencies = [ + "codespan", + "derive_more", + "logos", +] [[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +name = "planus-translation" +version = "1.1.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" +dependencies = [ + "bitflags", + "codespan", + "codespan-reporting", + "indexmap", + "lalrpop", + "lalrpop-util", + "num-traits", + "planus-lexer", + "planus-types", + "string-interner", +] [[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +name = "planus-types" +version = "1.1.1" +source = "git+https://github.com/swz-git/planus?rev=a0b1fbf#a0b1fbf28458389e2d3dc7598d257231103d9497" +dependencies = [ + "codespan", + "indexmap", + "lalrpop-util", + "planus-lexer", + "string-interner", +] [[package]] name = "portable-atomic" @@ -716,15 +646,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" -[[package]] -name = "potential_utf" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" -dependencies = [ - "zerovec", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -735,15 +656,10 @@ dependencies = [ ] [[package]] -name = "proc-macro-utils" -version = "0.10.0" +name = "precomputed-hash" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" -dependencies = [ - "proc-macro2", - "quote", - "smallvec", -] +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" @@ -816,61 +732,6 @@ dependencies = [ "syn", ] -[[package]] -name = "quinn" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" -dependencies = [ - "bytes", - "getrandom 0.3.3", - "lru-slab", - "rand", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - [[package]] name = "quote" version = "1.0.40" @@ -880,28 +741,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "quote-use" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" -dependencies = [ - "quote", - "quote-use-macros", -] - -[[package]] -name = "quote-use-macros" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" -dependencies = [ - "proc-macro-utils", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "r-efi" version = "5.3.0" @@ -934,129 +773,77 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom", ] [[package]] -name = "reqwest" -version = "0.12.20" +name = "random_color" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "d635c5e80ae160390ac62ca027d2d06c94c1dc69e5c0a12f1e3a53664dc84966" dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", + "rand", ] [[package]] -name = "ring" -version = "0.17.14" +name = "redox_syscall" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", + "bitflags", ] [[package]] -name = "rlbot_flatbuffers" -version = "0.17.1" +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ - "flatbuffers", - "get-size", - "pyo3", - "reqwest", - "serde", - "zip", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "rustc-demangle" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" - -[[package]] -name = "rustc-hash" -version = "2.1.1" +name = "regex-automata" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ - "semver", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "rustls" -version = "0.23.28" +name = "regex-syntax" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +name = "rlbot_flatbuffers" +version = "0.18.0" dependencies = [ - "web-time", - "zeroize", + "eyre", + "indexmap", + "planus", + "planus-codegen", + "planus-translation", + "planus-types", + "pyo3", + "serde", ] [[package]] -name = "rustls-webpki" -version = "0.103.3" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "semver", ] [[package]] @@ -1066,10 +853,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] -name = "ryu" -version = "1.0.20" +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" @@ -1098,46 +894,20 @@ dependencies = [ ] [[package]] -name = "serde_json" -version = "1.0.140" +name = "sha3" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", + "digest", + "keccak", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "slab" -version = "0.4.10" +name = "siphasher" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "smallvec" @@ -1146,26 +916,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "socket2" -version = "0.5.10" +name = "string-interner" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0" dependencies = [ - "libc", - "windows-sys 0.52.0", + "hashbrown", + "serde", ] [[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "subtle" -version = "2.6.1" +name = "string_cache" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] [[package]] name = "syn" @@ -1179,30 +949,29 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "target-lexicon" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] -name = "synstructure" -version = "0.13.2" +name = "term" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "8a984c8d058c627faaf5e8e2ed493fa3c51771889196de1016cf9c1c6e90d750" dependencies = [ - "proc-macro2", - "quote", - "syn", + "home", + "windows-sys", ] [[package]] -name = "target-lexicon" -version = "0.13.2" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] [[package]] name = "thiserror" @@ -1225,130 +994,40 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" +name = "typenum" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] -name = "tower-service" -version = "0.3.3" +name = "unicase" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] -name = "tracing" -version = "0.1.41" +name = "unicode-ident" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-core", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] -name = "tracing-core" -version = "0.1.34" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", -] +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "try-lock" -version = "0.2.5" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "unicode-ident" -version = "1.0.18" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unindent" @@ -1357,43 +1036,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" [[package]] -name = "untrusted" -version = "0.9.0" +name = "vec_map" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] -name = "url" -version = "2.5.4" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "try-lock", + "same-file", + "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" @@ -1404,112 +1067,12 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "1.0.1" +name = "winapi-util" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", + "windows-sys", ] [[package]] @@ -1594,36 +1157,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "writeable" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" - -[[package]] -name = "yoke" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.8.26" @@ -1643,95 +1176,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" -dependencies = [ - "arbitrary", - "crc32fast", - "flate2", - "indexmap", - "memchr", - "zopfli", -] - -[[package]] -name = "zlib-rs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" - -[[package]] -name = "zopfli" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index d1048d8..86c5d1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rlbot_flatbuffers" -version = "0.17.1" +version = "0.18.0" edition = "2024" description = "A Python module implemented in Rust for serializing and deserializing RLBot's flatbuffers" repository = "https://github.com/RLBot/flatbuffers-python" @@ -19,15 +19,15 @@ crate-type = ["cdylib"] [dependencies] pyo3 = "0.25.0" -serde = "1.0.217" -flatbuffers = "=25.2.10" -# get-size appears to be unmaintained but it's too useful here -# forked and updated deps -get-size = { git = "https://github.com/VirxEC/get-size", branch = "update", features = ["derive"] } +serde = { version = "1.0.219", features = ["derive"] } +planus = { git = "https://github.com/swz-git/planus", rev = "a0b1fbf" } [build-dependencies] -reqwest = { version = "0.12.15", features = ["blocking", "rustls-tls"], default-features = false } -zip = { version = "4.0.0", features = ["deflate"], default-features = false } +indexmap = "2.10.0" +planus-translation = { git = "https://github.com/swz-git/planus", rev = "a0b1fbf" } +planus-codegen = { git = "https://github.com/swz-git/planus", rev = "a0b1fbf" } +planus-types = { git = "https://github.com/swz-git/planus", rev = "a0b1fbf" } +eyre = "0.6.12" [profile.dev] opt-level = 2 diff --git a/codegen/class_inject.rs b/codegen/class_inject.rs index acb15e9..ac21bfc 100644 --- a/codegen/class_inject.rs +++ b/codegen/class_inject.rs @@ -1,15 +1,11 @@ use std::{fs, io}; -use crate::PythonBindType; - -pub fn classes_to_lib_rs(type_data: &[PythonBindType]) -> io::Result<()> { - let mut class_names = type_data - .iter() - .map(|generator| format!(" {}", generator.struct_name())) - .collect::>(); - class_names.sort(); - - let file_contents = format!(" classes: [\n{}\n ],", class_names.join(",\n")); +pub fn classes_to_lib_rs(mut class_names: Vec<&str>) -> io::Result<()> { + class_names.sort_unstable(); + let file_contents = format!( + " classes: [\n {}\n ],", + class_names.join(",\n ") + ); let mut lib_rs = fs::read_to_string("src/lib.rs")?; @@ -22,7 +18,6 @@ pub fn classes_to_lib_rs(type_data: &[PythonBindType]) -> io::Result<()> { let end = lib_rs[start..].find("],").unwrap() + 2; lib_rs.replace_range(start..start + end, &file_contents); - fs::write("src/lib.rs", lib_rs)?; Ok(()) diff --git a/codegen/enums.rs b/codegen/enums.rs index 9c302f9..ed51ff6 100644 --- a/codegen/enums.rs +++ b/codegen/enums.rs @@ -1,50 +1,6 @@ -use crate::generator::Generator; -use std::{borrow::Cow, fs, path::Path}; - -pub struct CustomEnumType { - pub name: String, - pub raw_type: String, - pub value: Option, - pub snake_case_name: String, - pub doc_str: Option>, -} - -fn camel_to_snake_case(variable_name: &str) -> String { - let mut snake_case_parts: Vec = Vec::new(); - - let mut last_was_uppercase = false; - for c in variable_name.chars() { - if c.is_uppercase() { - if last_was_uppercase { - snake_case_parts.last_mut().unwrap().push(c.to_lowercase().next().unwrap()); - } else { - snake_case_parts.push(c.to_lowercase().to_string()); - } - - last_was_uppercase = true; - } else if c.is_ascii_digit() { - snake_case_parts.push(c.to_lowercase().to_string()); - last_was_uppercase = false; - } else { - snake_case_parts.last_mut().unwrap().push(c); - last_was_uppercase = false; - } - } - - snake_case_parts.join("_") -} - -pub enum EnumType { - Enum, - Union, -} - -pub struct EnumBindGenerator { - pub filename: String, - pub struct_name: String, - pub types: Vec, - file_contents: Vec>, -} +use indexmap::IndexMap; +use planus_types::intermediate::{EnumVariant, IntegerLiteral}; +use std::borrow::Cow; macro_rules! write_str { ($self:ident, $s:expr) => { @@ -58,222 +14,101 @@ macro_rules! write_fmt { }; } -impl EnumBindGenerator { - pub fn new(filename: String, struct_name: String, types: Vec) -> Option { - let file_contents = vec![ - Cow::Borrowed("use crate::{flat_err_to_py, generated::rlbot::flat};"), - Cow::Borrowed("use flatbuffers::root;"), - Cow::Borrowed( - "use pyo3::{Bound, PyResult, Python, exceptions::PyValueError, pyclass, pymethods, types::PyBytes};", - ), - Cow::Borrowed(""), - ]; - - Some(Self { - filename: filename.to_string(), - struct_name, - types, - file_contents, - }) - } - - pub fn raw_types_to_custom(raw_types: Vec<(&str, &str, Option>)>) -> Vec { - raw_types - .into_iter() - .map(|(name, raw_type, doc_str)| CustomEnumType { - name: name.to_string(), - raw_type: raw_type.to_string(), - value: None, - snake_case_name: String::new(), - doc_str, - }) - .collect() - } - - pub fn get_types(contents: &str, struct_name: &str) -> Option<(Vec, EnumType)> { - let struct_definition = format!("pub struct {struct_name}(pub u8);\n"); - let struct_pos = contents.find(&struct_definition)?; - - // find 'impl CollisionShape {\n' - let impl_definition = format!("impl {struct_name} {{\n"); - let impl_pos = contents[struct_pos..].find(&impl_definition)?; - - // the next lines should be in the format 'pub const {some value}: Self = Self(i);\n' - let mut file_line_start = struct_pos + impl_pos + impl_definition.len(); - let mut lines = contents[file_line_start..].split('\n'); - let mut types = Vec::new(); - let mut docs = Vec::new(); - - loop { - let line = lines.next()?; - let line_trim = line.trim(); - - if line_trim.is_empty() { +/// Examples of what this function does: +/// FriendlyFire => FriendlyFire +/// ContactFF => ContactFf +/// ContactSilent => ContactSilent +/// ContactFFSilent => ContactFfSilent +/// +/// If a string doesn't need to be updated, the original is returned +fn normalize_caps<'a>(input: &'a str) -> Cow<'a, str> { + let bytes = input.as_bytes(); + let mut i = 0; + + // check if changes need to be made + // if a change is be needed, + // `i` will be the location of where we need to start + while i < bytes.len() - 1 { + if bytes[i].is_ascii_uppercase() && bytes[i + 1].is_ascii_uppercase() { + if i + 2 == bytes.len() { break; } - if line_trim.starts_with("///") { - docs.push(line_trim.trim_start_matches("///").trim().to_string()); - file_line_start += line.len(); - continue; - } - - if line_trim.starts_with("//") { - file_line_start += line.len(); - docs.clear(); - continue; + if bytes[i + 2].is_ascii_uppercase() { + break; } - - let definition = line_trim.trim_start_matches("pub const ").trim_end_matches(';'); - - let mut parts = definition.split(": Self = "); - - let variable_name = parts.next()?; - let variable_value = parts.next()?.trim_start_matches("Self(").trim_end_matches(')'); - - let docs = if docs.is_empty() { - None - } else { - let docs_copy = docs.clone(); - docs.clear(); - - Some(docs_copy) - }; - - types.push((variable_name, variable_value, docs)); - - file_line_start += line.len(); - } - - if types.is_empty() { - return None; } - let mut custom_types = Self::raw_types_to_custom(types); - - let union_definition = format!("pub enum {struct_name}T {{\n"); - let Some(union_start) = contents.find(&union_definition) else { - return Some((custom_types, EnumType::Enum)); - }; - - let union_end_definition = "}\n"; - let union_end = contents[union_start..].find(union_end_definition).unwrap(); - - let union_definition = - &contents[union_start + union_definition.len()..union_start + union_end - union_end_definition.len()]; - - for (line, variable) in union_definition.split('\n').zip(&mut custom_types) { - let line_trim = line.trim().trim_start_matches(&variable.name).trim_end_matches(','); - - if line_trim.is_empty() { - variable.value = None; - continue; - } - - variable.snake_case_name = camel_to_snake_case(variable.name.as_str()); - - let new_type = line_trim.trim_start_matches("(Box<").trim_end_matches("T>)"); - variable.value = Some(new_type.to_string()); - } + i += 1; + } - Some((custom_types, EnumType::Union)) + if i == bytes.len() - 1 { + // no changes need to be made - return the original + return Cow::Borrowed(input); } - fn generate_new_method(&mut self) { - write_str!(self, " #[new]"); - assert!(u8::try_from(self.types.len()).is_ok()); + // changes must be made, `i` stores the location of the first change + let mut result = String::with_capacity(bytes.len()); + result.push_str(&input[..i]); - write_str!(self, " #[pyo3(signature = (value=Default::default()))]"); - write_str!(self, " pub fn new(value: u8) -> PyResult {"); - write_str!(self, " match value {"); + let mut chars = input.chars().skip(i); + while let Some(mut char) = chars.next() { + let mut num_upper = 0; - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - write_fmt!(self, " {} => Ok(Self::{variable_name}),", variable_info.raw_type); - } + loop { + let Some(next_char) = chars.next() else { + result.push(char.to_ascii_lowercase()); + break; + }; - if self.types.len() != usize::from(u8::MAX) { - write_str!( - self, - " v => Err(PyValueError::new_err(format!(\"Unknown value of {v}\")))," - ); + if next_char.is_ascii_uppercase() { + num_upper += 1; + result.push(if num_upper == 1 { + char + } else { + char.to_ascii_lowercase() + }); + char = next_char; + } else { + result.push(char); + result.push(next_char); + break; + } } - - write_str!(self, " }"); - write_str!(self, " }"); - } - - fn generate_str_method(&mut self) { - write_str!(self, " pub fn __str__(&self) -> String {"); - write_str!(self, " self.__repr__()"); - write_str!(self, " }"); } - fn generate_repr_method(&mut self) { - write_str!(self, " pub fn __repr__(&self) -> String {"); - write_fmt!(self, " format!(\"{}.{{self:?}}\")", self.struct_name); - write_str!(self, " }"); - } + Cow::Owned(result) } -impl Generator for EnumBindGenerator { - fn filename(&self) -> &str { - &self.filename - } - - fn struct_name(&self) -> &str { - &self.struct_name - } - - fn file_contents(&self) -> &Vec> { - &self.file_contents - } - - fn modify_source(&self, path: &Path) { - let mut contents = fs::read_to_string(path).unwrap(); - - #[cfg(windows)] - { - contents = contents.replace("\r\n", "\n"); - } - - contents = contents.replace("use self::flatbuffers", "use get_size::GetSize;\nuse self::flatbuffers"); - - contents = contents.replace( - "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]\n", - "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, GetSize)]\n", - ); - - let start = contents.find("impl core::fmt::Debug for ").unwrap(); - let end = contents[start..].find("\n}").unwrap() + 3; - contents.replace_range(start..start + end, ""); - - let start = contents.find(" pub fn variant_name").unwrap(); - let prev_line_start = contents[..start].rfind(";\n").unwrap(); - let end = contents[start..].find(" }\n").unwrap() + 7; - contents.replace_range(prev_line_start + 1..start + end, ""); - - for val in ["ENUM_MIN", "ENUM_MAX", "ENUM_VALUES"] { - let line_start = contents.find(&format!("\npub const {val}")).unwrap(); - let prev_line_start = contents[..line_start].rfind(";\n").unwrap(); - let line_end = contents[line_start..].find(";\n").unwrap(); +pub struct EnumBindGenerator<'a> { + name: &'a str, + variants: &'a IndexMap, + file_contents: Vec>, +} - contents.replace_range(prev_line_start..line_start + line_end, ""); +impl<'a> EnumBindGenerator<'a> { + pub fn new(name: &'a str, variants: &'a IndexMap) -> Self { + Self { + name, + variants, + file_contents: Vec::new(), } - - fs::write(path, contents).unwrap(); } fn generate_definition(&mut self) { - write_str!(self, "#[allow(non_camel_case_types)]"); - write_str!(self, "#[pyclass(module = \"rlbot_flatbuffers\", frozen, hash, eq, eq_int)]"); - write_str!(self, "#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]"); - write_fmt!(self, "pub enum {} {{", self.struct_name); + write_str!( + self, + "#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]" + ); + write_str!( + self, + "#[pyclass(module = \"rlbot_flatbuffers\", frozen, hash, eq, eq_int)]" + ); + write_fmt!(self, "pub enum {} {{", self.name); write_str!(self, " #[default]"); - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - write_fmt!(self, " {variable_name} = {},", variable_info.raw_type); + for (var_num, var_info) in self.variants { + write_fmt!(self, " {} = {var_num},", var_info.name); } write_str!(self, "}"); @@ -281,22 +116,20 @@ impl Generator for EnumBindGenerator { } fn generate_from_flat_impls(&mut self) { - write_fmt!(self, "impl From for {} {{", self.struct_name, self.struct_name); - write_fmt!(self, " fn from(flat_t: flat::{}) -> Self {{", self.struct_name); + write_fmt!(self, "impl From for {} {{", self.name, self.name); + write_fmt!(self, " fn from(flat_t: flat::{}) -> Self {{", self.name); write_str!(self, " match flat_t {"); - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - + for var_info in self.variants.values() { write_fmt!( self, - " flat::{}::{variable_name} => Self::{variable_name},", - self.struct_name + " flat::{}::{} => Self::{},", + self.name, + normalize_caps(&var_info.name), + var_info.name, ); } - write_fmt!(self, " _ => Self::{},", self.types.last().unwrap().name.as_str()); - write_str!(self, " }"); write_str!(self, " }"); write_str!(self, "}"); @@ -304,17 +137,17 @@ impl Generator for EnumBindGenerator { } fn generate_to_flat_impls(&mut self) { - write_fmt!(self, "impl From<&{}> for flat::{} {{", self.struct_name, self.struct_name); - write_fmt!(self, " fn from(py_type: &{}) -> Self {{", self.struct_name); - write_str!(self, " match *py_type {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); + write_fmt!(self, "impl From<{}> for flat::{} {{", self.name, self.name); + write_fmt!(self, " fn from(py_type: {}) -> Self {{", self.name); + write_str!(self, " match py_type {"); + for var_info in self.variants.values() { write_fmt!( self, - " {}::{variable_name} => Self::{variable_name},", - self.struct_name + " {}::{} => Self::{},", + self.name, + var_info.name, + normalize_caps(&var_info.name), ); } @@ -325,9 +158,47 @@ impl Generator for EnumBindGenerator { write_str!(self, ""); } + fn generate_new_method(&mut self) { + write_str!(self, " #[new]"); + assert!(u8::try_from(self.variants.len()).is_ok()); + + write_str!(self, " #[pyo3(signature = (value=Default::default()))]"); + write_str!(self, " pub fn new(value: u8) -> PyResult {"); + write_str!(self, " match value {"); + + for (var_num, var_info) in self.variants { + write_fmt!( + self, + " {} => Ok(Self::{}),", + var_num.to_u64(), + var_info.name + ); + } + + write_str!( + self, + " v => Err(PyValueError::new_err(format!(\"Unknown value of {v}\")))," + ); + + write_str!(self, " }"); + write_str!(self, " }"); + } + + fn generate_str_method(&mut self) { + write_str!(self, " pub fn __str__(&self) -> String {"); + write_str!(self, " self.__repr__()"); + write_str!(self, " }"); + } + + fn generate_repr_method(&mut self) { + write_str!(self, " pub fn __repr__(&self) -> String {"); + write_fmt!(self, " format!(\"{}.{{self:?}}\")", self.name); + write_str!(self, " }"); + } + fn generate_py_methods(&mut self) { write_str!(self, "#[pymethods]"); - write_fmt!(self, "impl {} {{", self.struct_name); + write_fmt!(self, "impl {} {{", self.name); self.generate_new_method(); write_str!(self, ""); @@ -339,4 +210,20 @@ impl Generator for EnumBindGenerator { write_str!(self, "}"); write_str!(self, ""); } + + pub fn generate_binds(mut self) -> Vec> { + write_str!(self, "use crate::flat;"); + write_str!( + self, + "use pyo3::{PyResult, exceptions::PyValueError, pyclass, pymethods};" + ); + write_str!(self, ""); + + self.generate_definition(); + self.generate_from_flat_impls(); + self.generate_to_flat_impls(); + self.generate_py_methods(); + + self.file_contents + } } diff --git a/codegen/generator.rs b/codegen/generator.rs deleted file mode 100644 index c5f697f..0000000 --- a/codegen/generator.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::PYTHON_OUT_FOLDER; -use std::{borrow::Cow, fs, io, path::Path}; - -pub trait Generator { - fn filename(&self) -> &str; - fn struct_name(&self) -> &str; - fn file_contents(&self) -> &Vec>; - - fn modify_source(&self, path: &Path); - fn generate_definition(&mut self); - fn generate_from_flat_impls(&mut self); - fn generate_to_flat_impls(&mut self); - fn generate_py_methods(&mut self); - - fn finish(&self) -> io::Result<()> { - let file_path = format!("{PYTHON_OUT_FOLDER}/{}.rs", self.filename()); - let file = Path::new(&file_path); - - fs::create_dir_all(file.parent().unwrap())?; - fs::write(file, self.file_contents().join("\n"))?; - - Ok(()) - } - - fn generate(&mut self, filepath: &Path) -> io::Result<()> { - self.modify_source(filepath); - self.generate_definition(); - self.generate_from_flat_impls(); - self.generate_to_flat_impls(); - self.generate_py_methods(); - self.finish()?; - - Ok(()) - } -} diff --git a/codegen/main.rs b/codegen/main.rs index be155b6..96747e3 100644 --- a/codegen/main.rs +++ b/codegen/main.rs @@ -1,298 +1,187 @@ -#![allow(clippy::literal_string_with_formatting_args)] +use eyre::ContextCompat; +use planus_types::{ast::IntegerType, intermediate::DeclarationKind}; +use std::{env::set_current_dir, fs, path::Path}; + +use crate::{ + enums::EnumBindGenerator, structs::StructBindGenerator, table::TableBindGenerator, + unions::UnionBindGenerator, +}; mod class_inject; mod enums; -mod generator; mod pyi; mod structs; +mod table; mod unions; -use generator::Generator; -use std::{borrow::Cow, env::set_current_dir, fs, io, path::Path, process::Command}; -use structs::StructBindGenerator; -use zip::ZipArchive; - -const FLATC_DOWNLOAD_URL: &str = "https://github.com/google/flatbuffers/releases/download/v25.2.10/"; - const SCHEMA_FOLDER: &str = "./flatbuffers-schema"; const SCHEMA_FOLDER_BACKUP: &str = "../flatbuffers-schema"; const RLBOT_FBS: &str = "schema/rlbot.fbs"; -const FLATC_BINARY: &str = if cfg!(windows) { - "binaries\\flatc.exe" -} else { - "binaries/flatc" -}; -const OUT_FOLDER: &str = "./src/generated"; +const OUT_FILE: &str = "./src/planus_flat.rs"; pub const PYTHON_OUT_FOLDER: &str = "./src/python"; -pub enum PythonBindType { - Struct(structs::StructBindGenerator), - Enum(enums::EnumBindGenerator), - Union(unions::UnionBindGenerator), +pub const FROZEN_TYPES: [&str; 26] = [ + "ControllableInfo", + "ControllableTeamInfo", + "PredictionSlice", + "BallPrediction", + "GoalInfo", + "BoostPad", + "FieldInfo", + "Physics", + "GamePacket", + "PlayerInfo", + "ScoreInfo", + "BallInfo", + "Touch", + "CollisionShape", + "BoxShape", + "SphereShape", + "CylinderShape", + "BoostPadState", + "MatchInfo", + "TeamInfo", + "Vector2", + "CoreMessage", + "InterfaceMessage", + "CorePacket", + "InterfacePacket", + "PlayerInput", +]; + +pub fn get_int_name(int_type: &IntegerType) -> &'static str { + match int_type { + IntegerType::U8 => "u8", + IntegerType::U16 => "u16", + IntegerType::U32 => "u32", + IntegerType::U64 => "u64", + IntegerType::I8 => "i8", + IntegerType::I16 => "i16", + IntegerType::I32 => "i32", + IntegerType::I64 => "i64", + } } -impl PythonBindType { - pub const BASE_TYPES: [&'static str; 6] = ["bool", "i32", "u32", "f32", "String", "u8"]; - pub const FROZEN_TYPES: [&'static str; 26] = [ - "ControllableInfo", - "ControllableTeamInfo", - "PredictionSlice", - "BallPrediction", - "GoalInfo", - "BoostPad", - "FieldInfo", - "Physics", - "GamePacket", - "PlayerInfo", - "ScoreInfo", - "BallInfo", - "Touch", - "CollisionShape", - "BoxShape", - "SphereShape", - "CylinderShape", - "BoostPadState", - "MatchInfo", - "TeamInfo", - "Vector2", - "CoreMessage", - "InterfaceMessage", - "CorePacket", - "InterfacePacket", - "PlayerInput", - ]; - pub const NO_SET_TYPES: [&'static str; 1] = ["PlayerClass"]; - pub const UNIONS: [&'static str; 6] = [ - "PlayerClass", - "CollisionShape", - "RelativeAnchor", - "RenderType", - "CoreMessage", - "InterfaceMessage", - ]; - - pub const OPTIONAL_UNIONS: [&'static str; 1] = ["RelativeAnchor"]; - pub const DEFAULT_OVERRIDES: [(&'static str, &'static str, &'static str); 1] = [("Color", "a", "255")]; - pub const FIELD_ALIASES: [(&'static str, &'static str, &'static str); 1] = [("PlayerInfo", "player_id", "spawn_id")]; - pub const FREELIST_TYPES: [(&'static str, usize); 0] = []; - - fn new(path: &Path) -> Option { - // get the filename without the extension - let filename = path.file_stem().unwrap().to_str().unwrap(); - - if filename == "mod" { - return None; - } - - // convert snake_case to CamelCase to get the struct name - let mut struct_name = String::new(); - for c in filename.split('_') { - struct_name.push_str(&c[..1].to_uppercase()); - struct_name.push_str(&c[1..]); - } - struct_name = struct_name - .replace("Rlbot", "RLBot") - .replace("Halign", "HAlign") - .replace("Valign", "VAlign"); - - let struct_t_name = format!("{struct_name}T"); - - let contents = fs::read_to_string(path).ok()?; - - #[cfg(windows)] - let contents = contents.replace("\r\n", "\n"); - - let struct_def = format!("pub struct {struct_name}"); - let struct_def_pos = contents.find(&struct_def).unwrap(); +fn camel_to_snake(input: &str) -> String { + let mut snake_case = String::new(); - let mut docs = Vec::new(); - - for line in contents[..struct_def_pos].lines().rev() { - if line.starts_with("///") { - docs.push(line.trim_start_matches("///").trim()); - } else { - break; + for (i, ch) in input.chars().enumerate() { + if ch.is_uppercase() { + if i != 0 { + snake_case.push('_'); } - } - - let struct_doc_str = if docs.is_empty() { - None + snake_case.push(ch.to_ascii_lowercase()); } else { - Some(docs.into_iter().map(|s| s.to_string()).rev().collect::>()) - }; - - if let Some(types) = StructBindGenerator::get_types(&contents, &struct_t_name) { - return Some(Self::Struct(StructBindGenerator::new( - filename.to_string(), - struct_name, - struct_t_name, - struct_doc_str, - contents, - types, - )?)); - } - - if let Some((types, enum_type)) = enums::EnumBindGenerator::get_types(&contents, &struct_name) { - return Some(match enum_type { - enums::EnumType::Enum => { - Self::Enum(enums::EnumBindGenerator::new(filename.to_string(), struct_name, types)?) - } - enums::EnumType::Union => Self::Union(unions::UnionBindGenerator::new( - filename.to_string(), - struct_name, - struct_t_name, - types, - )?), - }); - } - - None - } - - pub fn filename(&self) -> &str { - match self { - Self::Struct(bind) => bind.filename(), - Self::Enum(bind) => bind.filename(), - Self::Union(bind) => bind.filename(), - } - } - - pub fn struct_name(&self) -> &str { - match self { - Self::Struct(bind) => bind.struct_name(), - Self::Enum(bind) => bind.struct_name(), - Self::Union(bind) => bind.struct_name(), + snake_case.push(ch); } } - pub fn generate(&mut self, filepath: &Path) -> io::Result<()> { - match self { - Self::Struct(bind) => bind.generate(filepath), - Self::Enum(bind) => bind.generate(filepath), - Self::Union(bind) => bind.generate(filepath), - } - } + snake_case } -fn mod_rs_generator(type_data: &[PythonBindType]) -> io::Result<()> { - let mut file_contents = Vec::new(); - - for generator in type_data { - let filename = generator.filename(); - - file_contents.push(Cow::Owned(format!("mod {filename};"))); - file_contents.push(Cow::Owned(format!("pub use {filename}::*;"))); - } - - file_contents.push(Cow::Borrowed("")); - - fs::write(format!("{PYTHON_OUT_FOLDER}/mod.rs"), file_contents.join("\n"))?; - - Ok(()) -} - -fn run_flatc() { - println!("cargo:rerun-if-changed=flatbuffers-schema/comms.fbs"); - println!("cargo:rerun-if-changed=flatbuffers-schema/gamedata.fbs"); - println!("cargo:rerun-if-changed=flatbuffers-schema/gamestatemanip.fbs"); - println!("cargo:rerun-if-changed=flatbuffers-schema/matchconfig.fbs"); - println!("cargo:rerun-if-changed=flatbuffers-schema/rendering.fbs"); - println!("cargo:rerun-if-changed=flatbuffers-schema/rlbot.fbs"); - +fn main() -> eyre::Result<()> { set_current_dir(env!("CARGO_MANIFEST_DIR")).unwrap(); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/color.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/comms.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/corepacket.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/gamedata.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/gamestatemanip.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/interfacepacket.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/matchconfig.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/misc.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/rendering.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/rlbot.fbs"); + println!("cargo:rerun-if-changed=flatbuffers-schema/schema/vector.fbs"); + let mut schema_folder = Path::new(SCHEMA_FOLDER); if !schema_folder.exists() { schema_folder = Path::new(SCHEMA_FOLDER_BACKUP); - assert!(schema_folder.exists(), "Could not find flatbuffers schema folder"); + assert!( + schema_folder.exists(), + "Could not find flatbuffers schema folder" + ); } - let schema_folder_str = schema_folder.display(); - let flatc_str = format!("{schema_folder_str}/{FLATC_BINARY}"); - let flatc_path = Path::new(&flatc_str); + let rlbot_fbs_path = schema_folder.join(RLBOT_FBS); + let declarations = planus_translation::translate_files(&[rlbot_fbs_path.as_path()]) + .context("planus translation failed")?; - if !flatc_path.exists() { - fs::create_dir_all(flatc_path).unwrap(); + let python_folder = Path::new(PYTHON_OUT_FOLDER); + if !python_folder.exists() { + fs::create_dir(python_folder)?; + } - // if the flatc binary isn't found, download it - let file_name = if cfg!(windows) { - "Windows.flatc.binary.zip" - } else { - "Linux.flatc.binary.g++-13.zip" + let mut python_mod: Vec = Vec::with_capacity(declarations.declarations.len()); + let mut class_names: Vec<&str> = Vec::with_capacity(declarations.declarations.len()); + let mut python_files = Vec::with_capacity(declarations.declarations.len() + 1); + python_files.push(String::from("mod.rs")); + + // generate custom code + for (path, item) in &declarations.declarations { + let item_name = path.0.last().unwrap().as_str(); + let mut file_name = camel_to_snake(item_name); + + let file_contents = match &item.kind { + DeclarationKind::Table(info) => { + let bind_gen = + TableBindGenerator::new(item_name, &info.fields, &declarations.declarations); + bind_gen.generate_binds() + } + DeclarationKind::Struct(info) => { + let bind_gen = + StructBindGenerator::new(item_name, &info.fields, &declarations.declarations); + bind_gen.generate_binds() + } + DeclarationKind::Enum(info) => { + let bind_gen = EnumBindGenerator::new(item_name, &info.variants); + bind_gen.generate_binds() + } + DeclarationKind::Union(info) => { + let bind_gen = UnionBindGenerator::new(item_name, &info.variants); + bind_gen.generate_binds() + } + DeclarationKind::RpcService(_) => unimplemented!(), }; - let response = reqwest::blocking::get(format!("{FLATC_DOWNLOAD_URL}/{file_name}")) - .map_err(|e| { - eprintln!("Failed to download flatc binary: {e}"); - io::Error::other("Failed to download flatc binary") - }) - .unwrap(); - let bytes = response - .bytes() - .map_err(|e| { - eprintln!("Failed to read response stream when downloading flatc binary: {e}"); - io::Error::other("Failed to read response stream when downloading flatc binary") - }) - .unwrap(); - // extract zip - let mut zip = ZipArchive::new(io::Cursor::new(bytes)).unwrap(); - zip.extract(schema_folder).unwrap(); + class_names.push(item_name); + python_mod.push( + ["mod ", &file_name, ";\n", "pub use ", &file_name, "::*;\n"] + .into_iter() + .collect(), + ); + file_name.push_str(".rs"); - assert!(flatc_path.exists(), "Failed to download flatc binary"); + fs::write(python_folder.join(&file_name), file_contents.join("\n"))?; + python_files.push(file_name); } - let mut proc = Command::new(flatc_str); - - proc.args([ - "--rust".as_ref(), - "--gen-object-api".as_ref(), - "--gen-all".as_ref(), - "--filename-suffix".as_ref(), - "".as_ref(), - "--rust-module-root-file".as_ref(), - "-o".as_ref(), - OUT_FOLDER.as_ref(), - schema_folder.join(RLBOT_FBS).as_os_str(), - ]) - .spawn() - .unwrap() - .wait() - .unwrap(); - - assert!(proc.status().unwrap().success(), "flatc failed to run"); -} - -fn main() { - run_flatc(); - - let out_folder = Path::new(OUT_FOLDER).join("rlbot").join("flat"); + // remove old files for types that don't exist anymore + for item in fs::read_dir(python_folder)?.flatten() { + let os_file_name = item.file_name(); + let Some(file_name) = os_file_name.to_str() else { + continue; + }; - assert!( - out_folder.exists(), - "Could not find generated folder: {}", - out_folder.display() - ); + if python_files.iter().any(|item| item.as_str() == file_name) { + continue; + } - // read the current contents of the generated folder - let generated_files = fs::read_dir(out_folder) - .unwrap() - .map(|res| res.map(|e| e.path())) - .collect::, io::Error>>() - .unwrap(); + fs::remove_file(python_folder.join(file_name))?; + } - let mut type_data = Vec::new(); + python_mod.sort_unstable(); + fs::write( + python_folder.join("mod.rs"), + python_mod.into_iter().collect::(), + )?; - for path in generated_files { - let Some(mut bind_generator) = PythonBindType::new(&path) else { - continue; - }; + let generated_planus = planus_codegen::generate_rust(&declarations)?.replace("RlBot", "RLBot"); + fs::write(OUT_FILE, generated_planus.as_bytes())?; - bind_generator.generate(&path).unwrap(); - type_data.push(bind_generator); - } + class_inject::classes_to_lib_rs(class_names)?; + pyi::generator(&declarations.declarations)?; - mod_rs_generator(&type_data).unwrap(); - pyi::generator(&type_data).unwrap(); - class_inject::classes_to_lib_rs(&type_data).unwrap(); + Ok(()) } diff --git a/codegen/pyi.rs b/codegen/pyi.rs index d5cac7a..d379831 100644 --- a/codegen/pyi.rs +++ b/codegen/pyi.rs @@ -1,10 +1,12 @@ -use crate::{ - PythonBindType, - generator::Generator, - structs::{InnerOptionType, InnerVecType, RustType}, +use indexmap::IndexMap; +use planus_types::{ + ast::IntegerType, + intermediate::{AbsolutePath, AssignMode, Declaration, DeclarationKind, SimpleType, TypeKind}, }; use std::{borrow::Cow, fs, io}; +use crate::structs::DEFAULT_OVERRIDES; + macro_rules! write_str { ($self:ident, $s:expr) => { $self.push(Cow::Borrowed($s)) @@ -17,11 +19,11 @@ macro_rules! write_fmt { }; } -pub fn generator(type_data: &[PythonBindType]) -> io::Result<()> { +pub fn generator(type_data: &IndexMap) -> io::Result<()> { let mut file = vec![ Cow::Borrowed("from __future__ import annotations"), Cow::Borrowed(""), - Cow::Borrowed("from typing import Optional, Sequence"), + Cow::Borrowed("from typing import Sequence"), Cow::Borrowed(""), Cow::Borrowed("__doc__: str"), Cow::Borrowed("__version__: str"), @@ -30,32 +32,37 @@ pub fn generator(type_data: &[PythonBindType]) -> io::Result<()> { Cow::Borrowed(""), ]; - let primitive_map = [ - ("bool", "bool"), - ("i32", "int"), - ("u32", "int"), - ("f32", "float"), - ("String", "str"), - ("u8", "int"), - ("Vec", "bytes"), - ]; + // let primitive_map = [ + // ("bool", "bool"), + // ("i32", "int"), + // ("u32", "int"), + // ("f32", "float"), + // ("String", "str"), + // ("u8", "int"), + // ("Vec", "bytes"), + // ]; - let mut sorted_types: Vec<&PythonBindType> = type_data.iter().collect(); - sorted_types.sort_by(|a, b| a.struct_name().cmp(b.struct_name())); + let mut sorted_types: Vec<_> = type_data.iter().collect(); + sorted_types.sort_by(|(a, _), (b, _)| a.0.last().unwrap().cmp(b.0.last().unwrap())); - for item in sorted_types { - let type_name = item.struct_name(); + for (full_type_name, item) in sorted_types { + let type_name = full_type_name.0.last().unwrap(); write_fmt!(file, "class {type_name}:"); - match item { - PythonBindType::Union(bind) => { - let types = bind - .types - .iter() - .map(|variable_info| variable_info.name.as_str()) - .filter(|variable_name| *variable_name != "NONE") - .collect::>(); + if !item.docstrings.docstrings.is_empty() { + write_str!(file, " \"\"\""); + + for docstring in &item.docstrings.docstrings { + write_fmt!(file, " {}", docstring.value.trim()); + } + + write_str!(file, " \"\"\"\n"); + } + + match &item.kind { + DeclarationKind::Union(info) => { + let types: Vec<_> = info.variants.keys().cloned().collect(); let default_value = types.first().unwrap(); let union_str = types.join(" | "); @@ -68,21 +75,15 @@ pub fn generator(type_data: &[PythonBindType]) -> io::Result<()> { write_fmt!(file, " self, item: {union_str} = {default_value}()"); write_str!(file, " ): ...\n"); } - PythonBindType::Enum(bind) => { - for variable_info in &bind.types { - let variable_name = variable_info.name.as_str(); - if variable_name == "NONE" { - continue; - } + DeclarationKind::Enum(info) => { + for (var_val, var_info) in &info.variants { + write_fmt!(file, " {} = {type_name}({var_val})", var_info.name); - let variable_type = variable_info.raw_type.as_str(); - write_fmt!(file, " {variable_name} = {type_name}({variable_type})"); - - if let Some(docs) = variable_info.doc_str.as_ref() { + if !var_info.docstrings.docstrings.is_empty() { write_str!(file, " \"\"\""); - for line in docs { - write_fmt!(file, " {line}"); + for line in &var_info.docstrings.docstrings { + write_fmt!(file, " {}", line.value.trim()); } write_str!(file, " \"\"\""); @@ -93,221 +94,320 @@ pub fn generator(type_data: &[PythonBindType]) -> io::Result<()> { write_str!(file, " def __new__(cls, value: int = 0): ..."); write_str!(file, " def __init__(self, value: int = 0):"); write_str!(file, " \"\"\""); - write_str!(file, " :raises ValueError: If the `value` is not a valid enum value"); + write_str!( + file, + " :raises ValueError: If the `value` is not a valid enum value" + ); write_str!(file, " \"\"\""); write_str!(file, " def __int__(self) -> int: ..."); - write_fmt!(file, " def __eq__(self, other: {type_name}) -> bool: ..."); - write_str!(file, " def __hash__(self) -> str: ..."); + write_str!(file, " def __eq__(self, other) -> bool: ..."); + write_str!(file, " def __hash__(self) -> int: ..."); } - PythonBindType::Struct(bind) => { - if let Some(docs) = bind.struct_doc_str.as_ref() { - write_str!(file, " \"\"\""); + DeclarationKind::Struct(info) => { + for (field_name, field_info) in &info.fields { + let python_type = Cow::Borrowed(match &field_info.type_ { + SimpleType::Bool => "bool", + SimpleType::Float(_) => "float", + SimpleType::Integer(_) => "int", + SimpleType::Enum(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() + } + SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() + } + }); - for line in docs { - write_fmt!(file, " {line}"); - } + write_fmt!(file, " {field_name}: {python_type}"); + + if !field_info.docstrings.docstrings.is_empty() { + write_str!(file, " \"\"\""); + + for line in &field_info.docstrings.docstrings { + write_fmt!(file, " {}", line.value.trim()); + } - write_str!(file, " \"\"\""); + write_str!(file, " \"\"\""); + } } - let mut python_types = Vec::new(); + if info.fields.is_empty() { + write_str!(file, " def __init__(self): ..."); + continue; + } - 'outer: for variable_info in &bind.types { - let variable_name = variable_info.name.as_str(); - let variable_type = variable_info.raw_type.as_str(); + write_str!(file, ""); + write_str!(file, " __match_args__ = ("); - for (rust_type, python_type) in primitive_map { - if variable_type == rust_type { - python_types.push(python_type.to_string()); - write_fmt!(file, " {variable_name}: {python_type}"); + for field_name in info.fields.keys() { + write_fmt!(file, " \"{field_name}\","); + } + write_str!(file, " )"); + write_str!(file, ""); - if let Some(docs) = variable_info.doc_str.as_ref() { - write_str!(file, " \"\"\""); + let inits = [("new", "cls"), ("init", "self")]; - for line in docs { - write_fmt!(file, " {line}"); + let default_overrides: Vec<_> = DEFAULT_OVERRIDES + .into_iter() + .filter_map(|(struct_name, field_name, value)| { + if struct_name == type_name { + Some((field_name, value)) + } else { + None + } + }) + .collect(); + + for (func, first_arg) in inits { + write_fmt!(file, " def __{func}__("); + write_fmt!(file, " {first_arg},"); + + for (field_name, field_info) in &info.fields { + let python_type = Cow::Borrowed(match &field_info.type_ { + SimpleType::Bool => "bool", + SimpleType::Float(_) => "float", + SimpleType::Integer(_) => "int", + SimpleType::Enum(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() + } + SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + match path.0.last().unwrap().as_str() { + "Float" => "Float | float", + "Bool" => "Bool | bool", + name => name, } + } + }); + + if let Some((_, value)) = default_overrides + .iter() + .find(|(field, _)| field == field_name) + { + write_fmt!(file, " {field_name}: {python_type} = {value},"); + continue; + } - write_str!(file, " \"\"\""); + let default_value = match &field_info.type_ { + SimpleType::Bool => Cow::Borrowed("False"), + SimpleType::Float(_) => Cow::Borrowed("0.0"), + SimpleType::Integer(_) => Cow::Borrowed("0"), + SimpleType::Enum(idx) | SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("{name}()")) } + }; - continue 'outer; - } + write_fmt!( + file, + " {field_name}: {python_type} = {default_value}," + ); } - match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => { - python_types.push("bytes".to_string()); - write_fmt!(file, " {variable_name}: bytes"); - } - RustType::Vec(InnerVecType::Base(type_name)) => { - let python_type = if type_name == "bool" { - "bool" - } else if type_name == "i32" || type_name == "u32" { - "int" - } else if type_name == "f32" { - "float" - } else { - type_name - }; - - python_types.push(format!("Sequence[{python_type}]")); - write_fmt!(file, " {variable_name}: Sequence[{python_type}]"); - } - RustType::Vec(InnerVecType::String) => { - python_types.push("Sequence[str]".to_string()); - write_fmt!(file, " {variable_name}: Sequence[str]"); - } - RustType::Vec(InnerVecType::Custom(inner_type)) => { - python_types.push(format!("Sequence[{inner_type}]")); - write_fmt!(file, " {variable_name}: Sequence[{inner_type}]"); - } - RustType::Option(InnerOptionType::String, _) => { - python_types.push("Optional[str]".to_string()); - write_fmt!(file, " {variable_name}: Optional[str]"); - } - RustType::Option(InnerOptionType::BaseType, type_name) => { - let python_type = if type_name == "bool" { - "bool" - } else if type_name == "i32" || type_name == "u32" { - "int" - } else if type_name == "f32" { - "float" - } else { - type_name - }; - - python_types.push(format!("Optional[{python_type}]")); - write_fmt!(file, " {variable_name}: Optional[{python_type}]"); - } - RustType::Option(InnerOptionType::Custom, type_name) - | RustType::Option(InnerOptionType::Box, type_name) => { - write_fmt!(file, " {variable_name}: Optional[{type_name}]"); - - let python_type = if type_name == "Float" { - "Float | float" - } else if type_name == "Bool" { - "Bool | bool" - } else { - type_name.as_str() - }; - - python_types.push(format!("Optional[{python_type}]")); - } - RustType::Box(inner_type) => { - python_types.push(inner_type.to_string()); - write_fmt!(file, " {variable_name}: {inner_type}"); - } - RustType::String => { - python_types.push("str".to_string()); - write_fmt!(file, " {variable_name}: str"); - } - RustType::Union(type_name, is_optional) => { - if *is_optional { - write_fmt!(file, " {variable_name}: Optional[{type_name}]"); - } else { - write_fmt!(file, " {variable_name}: {type_name}"); + write_str!(file, " ): ..."); + } + } + DeclarationKind::Table(info) => { + for (field_name, field_info) in &info.fields { + let mut python_type = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => Cow::Borrowed(match simple_type { + SimpleType::Bool => "bool", + SimpleType::Float(_) => "float", + SimpleType::Integer(_) => "int", + SimpleType::Enum(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() } - - // search for the union with the name `type_name` and get the types - let union_types = type_data - .iter() - .find_map(|item| match item { - PythonBindType::Union(bind) if bind.struct_name() == type_name => { - Some(bind.types.iter().skip(1).map(|v| v.name.as_str()).collect::>()) - } - _ => None, - }) - .unwrap(); - - let python_type = union_types.join(" | "); - python_types.push(if *is_optional { - format!("Optional[{python_type}]") - } else { - python_type - }); - } - RustType::Custom(type_name) | RustType::Other(type_name) | RustType::Base(type_name) => { - python_types.push(type_name.to_string()); - write_fmt!(file, " {variable_name}: {type_name}"); + SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() + } + }), + TypeKind::Union(idx) | TypeKind::Table(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + Cow::Borrowed(path.0.last().unwrap().as_str()) } + TypeKind::String => Cow::Borrowed("str"), + TypeKind::Vector(inner_type) => match inner_type.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => Cow::Borrowed("Sequence[bool]"), + SimpleType::Float(_) => Cow::Borrowed("Sequence[float]"), + SimpleType::Integer(IntegerType::U8) => Cow::Borrowed("bytes"), + SimpleType::Integer(_) => Cow::Borrowed("Sequence[int]"), + SimpleType::Enum(idx) | SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap().as_str(); + Cow::Owned(format!("Sequence[{name}]")) + } + }, + TypeKind::String => Cow::Borrowed("Sequence[str]"), + TypeKind::Table(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap().as_str(); + Cow::Owned(format!("Sequence[{name}]")) + } + _ => unimplemented!(), + }, + _ => unimplemented!(), + }; + + if matches!(field_info.assign_mode, AssignMode::Optional) { + let mut new_type = python_type.to_string(); + new_type.push_str(" | None"); + python_type = Cow::Owned(new_type); } - if let Some(docs) = variable_info.doc_str.as_ref() { + write_fmt!(file, " {field_name}: {python_type}"); + + if !field_info.docstrings.docstrings.is_empty() { write_str!(file, " \"\"\""); - for line in docs { - write_fmt!(file, " {line}"); + for line in &field_info.docstrings.docstrings { + write_fmt!(file, " {}", line.value.trim()); } write_str!(file, " \"\"\""); } } - if !bind.types.is_empty() { - write_str!(file, ""); - write_str!(file, " __match_args__ = ("); - - for variable_info in &bind.types { - write_fmt!(file, " \"{}\",", variable_info.name); - } - write_str!(file, " )"); - } - - if bind.types.is_empty() { + if info.fields.is_empty() { write_str!(file, " def __init__(self): ..."); - } else { - write_str!(file, ""); - - let inits = [("new", "cls"), ("init", "self")]; + continue; + } - for (func, first_arg) in inits { - write_fmt!(file, " def __{func}__("); - write_fmt!(file, " {first_arg},"); + write_str!(file, ""); + write_str!(file, " __match_args__ = ("); - for (variable_info, python_type) in bind.types.iter().zip(&python_types) { - let variable_name = variable_info.name.as_str(); + for field_name in info.fields.keys() { + write_fmt!(file, " \"{field_name}\","); + } + write_str!(file, " )"); + write_str!(file, ""); - if let Some((field, value)) = bind.default_override { - if field == variable_name { - write_fmt!(file, " {variable_name}: {python_type} = {value},"); - continue; + let inits = [("new", "cls"), ("init", "self")]; + + for (func, first_arg) in inits { + write_fmt!(file, " def __{func}__("); + write_fmt!(file, " {first_arg},"); + + for (field_name, field_info) in &info.fields { + let mut python_type = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => Cow::Borrowed(match simple_type { + SimpleType::Bool => "bool", + SimpleType::Float(_) => "float", + SimpleType::Integer(_) => "int", + SimpleType::Enum(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + path.0.last().unwrap() + } + SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + match path.0.last().unwrap().as_str() { + "Float" => "Float | float", + "Bool" => "Bool | bool", + name => name, + } } + }), + TypeKind::Table(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + Cow::Borrowed(path.0.last().unwrap().as_str()) } + TypeKind::Union(idx) => { + let (_, info) = type_data.get_index(idx.0).unwrap(); + let DeclarationKind::Union(union_info) = &info.kind else { + unreachable!() + }; - let default_value = match variable_info.raw_type.as_str() { - "bool" => Cow::Borrowed("False"), - "i32" | "u32" | "f32" | "u8" => Cow::Borrowed("0"), - "String" => Cow::Borrowed("\"\""), - "Vec" => Cow::Borrowed("b\"\""), - t => { - if python_type.starts_with("Optional") || t.starts_with("Option<") { - Cow::Borrowed("None") - } else if let Some(pos) = python_type.find('|') { - Cow::Owned(format!("{}()", &python_type[..pos - 1])) - } else if t.starts_with("Vec<") { - Cow::Borrowed("[]") - } else if t.starts_with("Box<") { - let inner_type = - t.trim_start_matches("Box<").trim_end_matches('>').trim_end_matches('T'); - Cow::Owned(format!("{inner_type}()")) - } else { - Cow::Owned(format!("{}()", t.trim_end_matches('T'))) + let mut keys: Vec<_> = + union_info.variants.keys().cloned().collect(); + keys.sort_unstable(); + + Cow::Owned(keys.join(" | ")) + } + TypeKind::String => Cow::Borrowed("str"), + TypeKind::Vector(inner_type) => match inner_type.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => Cow::Borrowed("Sequence[bool]"), + SimpleType::Float(_) => Cow::Borrowed("Sequence[float]"), + SimpleType::Integer(IntegerType::U8) => Cow::Borrowed("bytes"), + SimpleType::Integer(_) => Cow::Borrowed("Sequence[int]"), + SimpleType::Enum(idx) | SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap().as_str(); + Cow::Owned(format!("Sequence[{name}]")) } + }, + TypeKind::String => Cow::Borrowed("Sequence[str]"), + TypeKind::Table(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap().as_str(); + Cow::Owned(format!("Sequence[{name}]")) } - }; - - write_fmt!(file, " {variable_name}: {python_type} = {default_value},"); + _ => unimplemented!(), + }, + _ => unimplemented!(), + }; + + if matches!(field_info.assign_mode, AssignMode::Optional) { + let mut new_type = python_type.to_string(); + new_type.push_str(" | None"); + python_type = Cow::Owned(new_type); } - write_str!(file, " ): ..."); + let default_value = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => Cow::Borrowed("False"), + SimpleType::Float(_) => Cow::Borrowed("0.0"), + SimpleType::Integer(_) => Cow::Borrowed("0"), + SimpleType::Enum(idx) | SimpleType::Struct(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("{name}()")) + } + }, + TypeKind::String => Cow::Borrowed("\"\""), + TypeKind::Vector(inner_type) => match &inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + Cow::Borrowed("bytes()") + } + _ => Cow::Borrowed("[]"), + }, + TypeKind::Table(idx) => { + let (path, _) = type_data.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("{name}()")) + } + TypeKind::Union(idx) => { + let (_, info) = type_data.get_index(idx.0).unwrap(); + let DeclarationKind::Union(union_info) = &info.kind else { + unreachable!() + }; + + let mut keys: Vec<_> = union_info.variants.keys().collect(); + keys.sort_unstable(); + + Cow::Owned(format!("{}()", keys[0])) + } + _ => unimplemented!(), + }; + + write_fmt!( + file, + " {field_name}: {python_type} = {default_value}," + ); } + + write_str!(file, " ): ..."); } write_str!(file, " def pack(self) -> bytes:"); write_str!(file, " \"\"\""); write_str!(file, " Serializes this instance into a byte array"); - write_str!(file, " \"\"\""); + write_str!(file, " \"\"\"\n"); write_str!(file, " @staticmethod"); write_fmt!(file, " def unpack(data: bytes) -> {type_name}:"); @@ -317,8 +417,9 @@ pub fn generator(type_data: &[PythonBindType]) -> io::Result<()> { file, " :raises InvalidFlatbuffer: If the `data` is invalid for this type" ); - write_str!(file, " \"\"\""); + write_str!(file, " \"\"\"\n"); } + _ => unimplemented!(), } write_str!(file, " def __str__(self) -> str: ..."); diff --git a/codegen/structs.rs b/codegen/structs.rs index b02f21e..1d70171 100644 --- a/codegen/structs.rs +++ b/codegen/structs.rs @@ -1,63 +1,7 @@ -use crate::{PythonBindType, generator::Generator}; -use std::{borrow::Cow, fs, iter::repeat_n, path::Path}; - -#[derive(Debug, PartialEq, Eq)] -pub enum InnerVecType { - U8, - Base(String), - String, - Custom(String), -} - -#[derive(Debug, PartialEq, Eq)] -pub enum InnerOptionType { - Box, - String, - BaseType, - Custom, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum RustType { - Vec(InnerVecType), - String, - Box(String), - Option(InnerOptionType, String), - Union(String, bool), - Custom(String), - Base(String), - Other(String), -} - -pub enum SpecialBase { - FloatT, - BoolT, -} - -pub struct CustomType { - pub name: String, - pub raw_type: String, - pub rust_type: RustType, - pub is_frozen: bool, - pub is_special_base: Option, - pub snake_case_name: String, - pub doc_str: Option>, -} - -pub struct StructBindGenerator { - pub filename: String, - pub struct_name: String, - struct_t_name: String, - pub struct_doc_str: Option>, - pub types: Vec, - file_contents: Vec>, - has_complex_pack: bool, - pub is_frozen: bool, - is_no_set: bool, - pub default_override: Option<(&'static str, &'static str)>, - freelist_size: usize, - aliases: Vec<(&'static str, &'static str)>, -} +use crate::{FROZEN_TYPES, get_int_name}; +use indexmap::IndexMap; +use planus_types::intermediate::{AbsolutePath, Declaration, SimpleType, StructField}; +use std::{borrow::Cow, iter::repeat_n}; macro_rules! write_str { ($self:ident, $s:expr) => { @@ -71,234 +15,218 @@ macro_rules! write_fmt { }; } -impl StructBindGenerator { +pub const DEFAULT_OVERRIDES: [(&str, &str, &str); 1] = [("Color", "a", "255")]; + +pub struct StructBindGenerator<'a> { + name: &'a str, + fields: &'a IndexMap, + all_items: &'a IndexMap, + default_overrides: Vec<(&'a str, &'a str)>, + file_contents: Vec>, + is_frozen: bool, +} + +impl<'a> StructBindGenerator<'a> { pub fn new( - filename: String, - struct_name: String, - struct_t_name: String, - struct_doc_str: Option>, - contents: String, - types: Vec, - ) -> Option { - let is_frozen = PythonBindType::FROZEN_TYPES.contains(&struct_name.as_str()); - let is_no_set = PythonBindType::NO_SET_TYPES.contains(&struct_name.as_str()); - let aliases: Vec<_> = PythonBindType::FIELD_ALIASES - .iter() - .filter_map(|&(name, real, alias)| { - if name == struct_name.as_str() { - Some((real, alias)) + name: &'a str, + fields: &'a IndexMap, + all_items: &'a IndexMap, + ) -> Self { + let default_overrides = DEFAULT_OVERRIDES + .into_iter() + .filter_map(|(struct_name, field_name, value)| { + if struct_name == name { + Some((field_name, value)) } else { None } }) .collect(); - let has_complex_pack = contents.contains("pub fn pack<'b, A: flatbuffers::Allocator + 'b>("); - - let mut file_contents = vec![]; - - file_contents.push(Cow::Borrowed(if types.is_empty() { - "use crate::{FromGil, flat_err_to_py, generated::rlbot::flat};" - } else { - "use crate::{FromGil, IntoGil, PyDefault, flat_err_to_py, generated::rlbot::flat};" - })); + Self { + name, + fields, + all_items, + default_overrides, + file_contents: Vec::new(), + is_frozen: FROZEN_TYPES.contains(&name), + } + } - if has_complex_pack { - file_contents.push(Cow::Borrowed("use flatbuffers::{FlatBufferBuilder, root};")); - file_contents.push(Cow::Borrowed("use get_size::GetSize;")); + fn generate_definition(&mut self) { + let pyclass_start_str = "#[pyclass(module = \"rlbot_flatbuffers\", subclass, "; + if self.is_frozen { + write_fmt!(self, "{pyclass_start_str}frozen, get_all)]"); + } else if self.fields.is_empty() { + write_fmt!(self, "{pyclass_start_str}frozen)]"); } else { - file_contents.push(Cow::Borrowed("use flatbuffers::root;")); + write_fmt!(self, "{pyclass_start_str}get_all)]"); } - file_contents.push(Cow::Borrowed("use pyo3::{prelude::*, types::*};")); - if !aliases.is_empty() { - file_contents.push(Cow::Borrowed("use std::sync::atomic::{AtomicBool, Ordering};")); + if self.fields.is_empty() { + write_str!(self, "#[derive(Default)]"); + write_fmt!(self, "pub struct {} {{}}", self.name); + write_str!(self, ""); + return; } - file_contents.push(Cow::Borrowed("")); + write_fmt!(self, "pub struct {} {{", self.name); - let default_override = PythonBindType::DEFAULT_OVERRIDES.iter().find_map(|&(name, field, value)| { - if name == struct_name.as_str() { - Some((field, value)) - } else { - None + for (field_name, field_info) in self.fields { + for docstring in &field_info.docstrings.docstrings { + write_fmt!(self, " ///{}", docstring.value); } - }); - let freelist_size = PythonBindType::FREELIST_TYPES - .iter() - .find_map(|&(name, size)| if name == struct_name.as_str() { Some(size) } else { None }) - .unwrap_or_default(); - - Some(Self { - filename, - struct_name, - struct_t_name, - struct_doc_str, - types, - file_contents, - has_complex_pack, - is_frozen, - is_no_set, - default_override, - freelist_size, - aliases, - }) - } + let mut add_set = !self.is_frozen; + let variable_type = match &field_info.type_ { + SimpleType::Bool => Cow::Borrowed("bool"), + SimpleType::Float(_) => { + add_set = false; + Cow::Borrowed("Py") + } + SimpleType::Integer(int_type) => Cow::Borrowed(get_int_name(int_type)), + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Py")) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; - pub fn get_types(contents: &str, struct_t_name: &str) -> Option> { - // find the struct definition - let struct_start_definition = format!("pub struct {struct_t_name} {{\n"); - let struct_start = contents.find(&struct_start_definition)?; + if add_set { + write_str!(self, " #[pyo3(set)]"); + } - let struct_end_definition = "}\n"; - let struct_end = contents[struct_start..].find(struct_end_definition).unwrap(); + write_fmt!(self, " pub {field_name}: {variable_type},"); + } - let start = struct_start + struct_start_definition.len(); - let end = struct_start + struct_end - struct_end_definition.len(); + write_str!(self, "}\n"); + write_fmt!(self, "impl crate::PyDefault for {} {{", self.name); + write_str!(self, " fn py_default(py: Python) -> Py {"); + write_str!(self, " Py::new(py, Self {"); + + for (field_name, field_type) in self.fields { + let end = match &field_type.type_ { + SimpleType::Float(_) => Cow::Borrowed("crate::pyfloat_default(py)"), + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}::py_default(py)")) + } + _ => Cow::Borrowed("Default::default()"), + }; - if end <= start { - return Some(Vec::new()); + write_fmt!(self, " {field_name}: {end},"); } - let struct_definition = &contents[start..end]; - - let raw_types: Vec<_> = struct_definition - .split('\n') - .filter_map(|s| { - let (name, raw_type) = s - .trim_start_matches(' ') - .trim_start_matches("pub ") - .trim_end_matches(',') - .split_once(": ")?; - - let var_def = format!("pub fn {name}("); - let var_def_pos = contents.find(&var_def).unwrap(); - - let mut docs = Vec::new(); - - for line in contents[..var_def_pos].lines().rev().skip(2) { - let line = line.trim(); - if line.starts_with("///") { - docs.push(line.trim_start_matches("///").trim()); - } else { - break; - } - } + write_str!(self, " }).unwrap()"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + } - let struct_doc_str = if docs.is_empty() { - None - } else { - Some(docs.into_iter().map(|s| s.to_string()).rev().collect::>()) - }; + fn generate_from_flat_impls(&mut self) { + let impl_type = format!("flat::{}", self.name); - Some((name, raw_type, struct_doc_str)) - }) - .collect(); + if self.fields.is_empty() { + write_fmt!(self, "impl From<{impl_type}> for {} {{", self.name); + write_fmt!(self, " fn from(_: {impl_type}) -> Self {{"); + write_fmt!(self, " {} {{}}", self.name); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + return; + } - let custom_types = Self::raw_types_to_custom(raw_types); + write_fmt!(self, "impl FromGil<{impl_type}> for {} {{", self.name); + + write_str!(self, " #[allow(unused_variables)]"); + write_fmt!( + self, + " fn from_gil(py: Python, flat_t: {impl_type}) -> Self {{" + ); + write_fmt!(self, " {} {{", self.name); + + for (field_name, field_info) in self.fields { + match &field_info.type_ { + SimpleType::Float(_) => { + write_fmt!( + self, + " {field_name}: crate::float_to_py(py, flat_t.{field_name})," + ) + } + SimpleType::Bool | SimpleType::Integer(_) => { + write_fmt!(self, " {field_name}: flat_t.{field_name},") + } + SimpleType::Struct(_) => { + write_fmt!( + self, + " {field_name}: crate::into_py_from(py, flat_t.{field_name}),", + ); + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + } - Some(custom_types) + write_str!(self, " }"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); } - fn raw_types_to_custom(raw_types: Vec<(&str, &str, Option>)>) -> Vec { - raw_types - .into_iter() - .map(|(name, raw_type, doc_str)| { - let (rust_type, inner_type) = if raw_type.starts_with("Vec<") { - if raw_type == "Vec" { - (RustType::Vec(InnerVecType::U8), None) - } else if raw_type == "Vec" { - (RustType::Vec(InnerVecType::String), None) - } else { - let inner_type = raw_type - .trim_start_matches("Vec<") - .trim_end_matches('>') - .trim_end_matches('T'); - - ( - RustType::Vec(if PythonBindType::BASE_TYPES.contains(&inner_type) { - InnerVecType::Base(inner_type.to_string()) - } else { - InnerVecType::Custom(inner_type.to_string()) - }), - Some(inner_type), - ) - } - } else if raw_type.starts_with("Option<") { - let inner = raw_type.trim_start_matches("Option<").trim_end_matches('>'); - - if inner.starts_with("Box<") { - let inner_type = inner.trim_start_matches("Box<").trim_end_matches('>').trim_end_matches('T'); - ( - RustType::Option(InnerOptionType::Box, inner_type.to_string()), - Some(inner_type), - ) - } else if inner == "String" { - (RustType::Option(InnerOptionType::String, inner.to_string()), None) - } else if PythonBindType::BASE_TYPES.contains(&inner) { - (RustType::Option(InnerOptionType::BaseType, inner.to_string()), None) - } else { - let inner = inner.trim_end_matches('T'); - (RustType::Option(InnerOptionType::Custom, inner.to_string()), Some(inner)) - } - } else if raw_type == "String" { - (RustType::String, None) - } else if raw_type.starts_with("Box<") { - let inner_type = raw_type - .trim_start_matches("Box<") - .trim_end_matches('>') - .trim_end_matches('T'); - (RustType::Box(inner_type.to_string()), Some(inner_type)) - } else if raw_type.ends_with('T') { - let inner_type = raw_type.trim_end_matches('T'); - - if PythonBindType::UNIONS.contains(&inner_type) { - ( - RustType::Union(inner_type.to_string(), PythonBindType::OPTIONAL_UNIONS.contains(&inner_type)), - Some(inner_type), - ) - } else { - (RustType::Custom(inner_type.to_string()), Some(inner_type)) - } - } else if PythonBindType::BASE_TYPES.contains(&raw_type) { - (RustType::Base(raw_type.to_string()), None) - } else { - (RustType::Other(raw_type.to_string()), Some(raw_type)) - }; - - let (is_frozen, is_special_base) = if let Some(inner_type) = inner_type { - let is_frozen = PythonBindType::FROZEN_TYPES.contains(&inner_type); - let is_special_base = if inner_type == "Float" { - Some(SpecialBase::FloatT) - } else if inner_type == "Bool" { - Some(SpecialBase::BoolT) - } else { - None - }; - - (is_frozen, is_special_base) - } else { - (false, None) - }; - - CustomType { - name: name.to_string(), - raw_type: raw_type.to_string(), - rust_type, - is_frozen, - is_special_base, - snake_case_name: String::new(), - doc_str, + fn generate_to_flat_impls(&mut self) { + let impl_type = format!("flat::{}", self.name); + + if self.fields.is_empty() { + write_fmt!(self, "impl From<&{}> for {impl_type} {{", self.name); + write_fmt!(self, " fn from(_: &{}) -> Self {{", self.name); + write_str!(self, " Self {}"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + return; + } + + write_fmt!(self, "impl FromGil<&{}> for {impl_type} {{", self.name); + write_str!(self, " #[allow(unused_variables)]"); + write_fmt!( + self, + " fn from_gil(py: Python, py_type: &{}) -> Self {{", + self.name + ); + write_str!(self, " Self {"); + + for (field_name, field_info) in self.fields { + match &field_info.type_ { + SimpleType::Float(_) => { + write_fmt!( + self, + " {field_name}: crate::float_from_py(py, &py_type.{field_name})," + ); } - }) - .collect() + SimpleType::Bool | SimpleType::Integer(_) => { + write_fmt!(self, " {field_name}: py_type.{field_name},"); + } + SimpleType::Struct(_) => { + write_fmt!( + self, + " {field_name}: crate::from_py_into(py, &py_type.{field_name}),", + ); + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + } + + write_str!(self, " }"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); } fn generate_new_method(&mut self) { write_str!(self, " #[new]"); - if self.types.is_empty() { + if self.fields.is_empty() { write_str!(self, " pub fn new() -> Self {"); write_str!(self, " Self {}"); write_str!(self, " }"); @@ -308,236 +236,106 @@ impl StructBindGenerator { let mut signature_parts = Vec::new(); let mut needs_python = false; - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if let Some((field, value)) = self.default_override { - if field == variable_name { - signature_parts.push(format!("{variable_name}={value}")); - continue; - } + for (field_name, field_info) in self.fields { + if let Some((_, default_value)) = self + .default_overrides + .iter() + .find(|(override_field_name, _)| override_field_name == field_name) + { + signature_parts.push(format!("{field_name}={default_value}")); + continue; } - let sig_part = match &variable_info.rust_type { - RustType::Option(_, _) => { - if variable_info.is_special_base.is_some() { - needs_python = true; - } - - format!("{variable_name}=None") - } - RustType::Union(_, _) - | RustType::Box(_) - | RustType::Custom(_) - | RustType::Vec(InnerVecType::U8) - | RustType::Vec(InnerVecType::Custom(_)) - | RustType::String => { + signature_parts.push(match &field_info.type_ { + SimpleType::Bool => format!("{field_name}=false"), + SimpleType::Integer(_) => format!("{field_name}=0"), + SimpleType::Float(_) => { needs_python = true; - format!("{variable_name}=None") + format!("{field_name}=0.0") } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => { - needs_python = true; - format!("{variable_name}=Default::default()") - } - _ => format!("{variable_name}=Default::default()"), - }, _ => { - format!("{variable_name}=Default::default()") + needs_python = true; + format!("{field_name}=None") } - }; - - signature_parts.push(sig_part); + }); } let max_num_types = if needs_python { 6 } else { 7 }; - if self.types.len() > max_num_types { + if self.fields.len() > max_num_types { write_str!(self, " #[allow(clippy::too_many_arguments)]"); } - write_fmt!(self, " #[pyo3(signature = ({}))]", signature_parts.join(", ")); + write_fmt!( + self, + " #[pyo3(signature = ({}))]", + signature_parts.join(", ") + ); write_str!(self, " pub fn new("); if needs_python { write_str!(self, " py: Python,"); } - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - let variable_type = match &variable_info.rust_type { - RustType::Vec(InnerVecType::Custom(_)) => Cow::Borrowed("Option>"), - RustType::Vec(InnerVecType::Base(inner_type)) => Cow::Owned(format!("Vec<{}>", inner_type)), - RustType::Vec(InnerVecType::String) => Cow::Borrowed("Vec"), - RustType::Vec(InnerVecType::U8) => Cow::Borrowed("Option>"), - RustType::Box(inner_type) => Cow::Owned(format!("Option>")), - RustType::Option(InnerOptionType::BaseType, inner_type) => Cow::Owned(format!("Option<{inner_type}>")), - RustType::Option(InnerOptionType::String, _) => Cow::Borrowed("Option>"), - RustType::Option(_, inner_type) => { - if inner_type == "Float" { - Cow::Borrowed("Option") - } else if inner_type == "Bool" { - Cow::Borrowed("Option") - } else { - Cow::Owned(format!("Option>")) - } - } - RustType::Base(inner_type) => Cow::Borrowed(match inner_type.as_str() { - "f32" => "f64", - item => item, - }), - RustType::String => Cow::Borrowed("Option>"), - RustType::Union(inner_type, _) => Cow::Owned(format!("Option")), - RustType::Custom(inner_type) => Cow::Owned(format!("Option>")), - RustType::Other(inner_type) => Cow::Owned(format!("super::{inner_type}")), + for (field_name, field_info) in self.fields { + let variable_type = match &field_info.type_ { + SimpleType::Bool => Cow::Borrowed("bool"), + SimpleType::Integer(int_type) => Cow::Borrowed(get_int_name(int_type)), + SimpleType::Float(_) => Cow::Borrowed("f64"), + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Option>")) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), }; - write_fmt!(self, " {variable_name}: {variable_type},"); + write_fmt!(self, " {field_name}: {variable_type},"); } write_str!(self, " ) -> Self {"); write_str!(self, " Self {"); - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if variable_info.is_special_base.is_some() { - write_fmt!(self, " {variable_name}: {variable_name}.map(|x| x.into_gil(py)),"); - continue; - } - - match &variable_info.rust_type { - RustType::Union(inner_type, is_optional) => { - let end = if *is_optional { - Cow::Borrowed("") - } else { - Cow::Owned(format!(".unwrap_or_else(|| super::{inner_type}::py_default(py))")) - }; - + for (field_name, field_info) in self.fields { + match &field_info.type_ { + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); write_fmt!( self, - " {variable_name}: {variable_name}.map(|u| Py::new(py, super::{inner_type}::new(u)).unwrap()){end}," + " {field_name}: {field_name}.unwrap_or_else(|| super::{name}::py_default(py))," ); } - RustType::Box(inner_type) | RustType::Custom(inner_type) => { + SimpleType::Float(_) => { write_fmt!( self, - " {variable_name}: {variable_name}.unwrap_or_else(|| super::{inner_type}::py_default(py))," - ); - } - RustType::Vec(InnerVecType::U8) => { - write_fmt!( - self, - " {variable_name}: {variable_name}.unwrap_or_else(|| PyBytes::new(py, &[]).unbind())," - ); - } - RustType::Vec(InnerVecType::Custom(_)) => { - write_fmt!( - self, - " {variable_name}: {variable_name}.unwrap_or_else(|| PyList::empty(py).unbind())," - ); - } - RustType::String => { - write_fmt!( - self, - " {variable_name}: {variable_name}.unwrap_or_else(|| crate::pydefault_string(py))," - ); + " {field_name}: PyFloat::new(py, {field_name}).unbind()," + ) } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => write_fmt!( - self, - " {variable_name}: PyFloat::new(py, {variable_name}).unbind()," - ), - _ => write_fmt!(self, " {variable_name},"), - }, - _ => write_fmt!(self, " {variable_name},"), + _ => write_fmt!(self, " {field_name},"), } } write_str!(self, " }"); write_str!(self, " }"); - for (real_name, alias) in &self.aliases { - let variable_info = self.types.iter().find(|info| info.name == *real_name).unwrap(); - - let variable_type = match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => String::from("Py"), - RustType::Vec(InnerVecType::String) => String::from("Vec"), - RustType::Vec(InnerVecType::Base(inner_type)) => format!("Vec<{}>", inner_type), - RustType::Vec(InnerVecType::Custom(_)) => String::from("Py"), - RustType::Box(inner_type) => format!("Py"), - RustType::Option(InnerOptionType::BaseType, inner_type) => { - format!("Option>") - } - RustType::Option(InnerOptionType::String, _) => String::from("Option>"), - RustType::Union(inner_type, true) | RustType::Option(_, inner_type) => { - format!("Option>") - } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => String::from("Py"), - _ => inner_type.clone(), - }, - RustType::String => String::from("Py"), - RustType::Union(inner_type, false) | RustType::Custom(inner_type) => { - format!("Py") - } - RustType::Other(inner_type) => format!("super::{inner_type}"), - }; - - write_str!(self, "\n #[getter]"); - write_fmt!(self, " pub fn get_{alias}(&self) -> {variable_type} {{"); - write_str!(self, " static PRINTED_WARNING: AtomicBool = AtomicBool::new(false);"); - write_str!( - self, - " if PRINTED_WARNING.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {" - ); - write_fmt!( - self, - " println!(\"WARNING: '{alias}' getter accessed, which is deprecated in favor of '{real_name}'.\");" - ); - write_str!(self, " }"); - write_str!(self, ""); - write_fmt!(self, " self.{real_name}"); - write_str!(self, " }"); - - if self.is_frozen || self.is_no_set { - continue; - } - - write_str!(self, "\n #[setter]"); - write_fmt!(self, " pub fn set_{alias}(&mut self, new: {variable_type}) {{"); - write_str!(self, " static PRINTED_WARNING: AtomicBool = AtomicBool::new(false);"); - write_str!( - self, - " if PRINTED_WARNING.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {" - ); - write_fmt!( - self, - " println!(\"WARNING: '{alias}' setter accessed, which is deprecated in favor of '{real_name}'.\");" - ); - write_str!(self, " }"); - write_str!(self, ""); - write_fmt!(self, " self.{real_name} = new;"); - write_str!(self, " }"); - } - - if self.is_frozen || self.is_no_set { + if self.is_frozen { return; } - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - match &variable_info.rust_type { - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => { - write_str!(self, "\n #[setter]"); - write_fmt!(self, " pub fn {variable_name}(&mut self, py: Python, value: f64) {{",); - write_fmt!(self, " self.{variable_name} = PyFloat::new(py, value).unbind();"); - write_str!(self, " }"); - } - _ => continue, - }, + for (field_name, field_info) in self.fields { + match &field_info.type_ { + SimpleType::Float(_) => { + write_str!(self, "\n #[setter]"); + write_fmt!( + self, + " pub fn {field_name}(&mut self, py: Python, value: f64) {{", + ); + write_fmt!( + self, + " self.{field_name} = PyFloat::new(py, value).unbind();" + ); + write_str!(self, " }"); + } _ => continue, } } @@ -550,9 +348,9 @@ impl StructBindGenerator { } fn generate_repr_method(&mut self) { - if self.types.is_empty() { + if self.fields.is_empty() { write_str!(self, " pub fn __repr__(&self, _py: Python) -> String {"); - write_fmt!(self, " String::from(\"{}()\")", self.struct_name); + write_fmt!(self, " String::from(\"{}()\")", self.name); write_str!(self, " }"); return; } @@ -562,104 +360,28 @@ impl StructBindGenerator { write_str!(self, " format!("); let repr_signature = self - .types + .fields .iter() - .map(|variable_info| { - let variable_name = variable_info.name.as_str(); - - match &variable_info.rust_type { - RustType::String => format!("{variable_name}={{:?}}"), - RustType::Vec(InnerVecType::U8) => format!("{variable_name}=bytes([{{}}])"), - RustType::Vec(_) => format!("{variable_name}=[{{}}]"), - _ => format!("{variable_name}={{}}"), - } - }) + .map(|(field_name, _)| format!("{field_name}={{}}")) .collect::>() .join(", "); - write_fmt!(self, " \"{}({repr_signature})\",", self.struct_name); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - match &variable_info.rust_type { - RustType::Vec(inner_type) => { - write_fmt!(self, " self.{variable_name}"); - match inner_type { - InnerVecType::U8 => { - write_str!(self, " .as_bytes(py)"); - write_str!(self, " .iter()"); - write_str!(self, " .map(ToString::to_string)"); - } - InnerVecType::String => { - write_str!(self, " .iter()"); - write_str!(self, " .map(|s| format!(\"{s:?}\"))"); - } - InnerVecType::Base(_) => { - write_str!(self, " .iter()"); - write_str!(self, " .map(ToString::to_string)"); - } - InnerVecType::Custom(type_name) => { - write_str!(self, " .bind_borrowed(py)"); - write_str!(self, " .iter()"); - write_fmt!( - self, - " .map(|x| x.downcast_into::().unwrap().borrow().__repr__(py))" - ); - } - } - write_str!(self, " .collect::>()"); - write_str!(self, " .join(\", \"),"); - } - RustType::Option(inner_type, _) => { - write_fmt!(self, " self.{variable_name}"); - write_str!(self, " .as_ref()"); - write_str!(self, " .map_or_else(crate::none_str, |i| {"); - - match inner_type { - InnerOptionType::BaseType => { - write_str!(self, " format!(\"{i:?}\")"); - } - InnerOptionType::String => { - write_str!( - self, - " format!(\"{:?}\", i.to_str(py).unwrap().to_string())" - ); - } - _ => { - write_str!(self, " i.borrow(py).__repr__(py)"); - } - } - - write_str!(self, " }),"); - } - RustType::Union(_, is_optional) => { - if *is_optional { - write_fmt!(self, " self.{variable_name}"); - write_str!(self, " .as_ref()"); - write_str!( - self, - " .map_or_else(crate::none_str, |i| i.borrow(py).inner_repr(py))," - ); - } else { - write_fmt!(self, " self.{variable_name}.borrow(py).inner_repr(py),"); - } - } - RustType::Box(_) | RustType::Custom(_) => { - write_fmt!(self, " self.{variable_name}.borrow(py).__repr__(py),"); - } - RustType::Base(inner_type) => { - if inner_type == "bool" { - write_fmt!(self, " crate::bool_to_str(self.{variable_name}),"); - } else { - write_fmt!(self, " self.{variable_name},"); - } + write_fmt!(self, " \"{}({repr_signature})\",", self.name); + + for (field_name, field_info) in self.fields { + match &field_info.type_ { + SimpleType::Struct(_) => { + write_fmt!( + self, + " self.{field_name}.borrow(py).__repr__(py)," + ); } - RustType::String => { - write_fmt!(self, " self.{variable_name}.bind(py).to_cow().unwrap(),"); + SimpleType::Bool => { + write_fmt!(self, " crate::bool_to_str(self.{field_name}),"); } - RustType::Other(_) => { - write_fmt!(self, " self.{variable_name}.__repr__(),"); + SimpleType::Integer(_) | SimpleType::Float(_) => { + write_fmt!(self, " self.{field_name},"); } + _ => write_fmt!(self, " self.{field_name}.__repr__(),"), } } @@ -669,453 +391,48 @@ impl StructBindGenerator { fn generate_long_args(&mut self) { write_str!(self, " #[classattr]"); - write_str!(self, " fn __match_args__(py: Python) -> Bound {"); + write_str!( + self, + " fn __match_args__(py: Python) -> Bound {" + ); write_str!(self, " pyo3::types::PyTuple::new(py, ["); - for variable_info in &self.types { - write_fmt!(self, " \"{}\",", variable_info.name); + for field_name in self.fields.keys() { + write_fmt!(self, " \"{field_name}\","); } write_str!(self, " ]).unwrap()"); write_str!(self, " }\n"); - write_str!(self, " #[classattr]"); - write_str!(self, " fn __slots__(py: Python) -> Bound {"); - write_str!(self, " Self::__match_args__(py)"); - write_str!(self, " }\n"); } fn generate_args(&mut self) { - if self.types.is_empty() { + if self.fields.is_empty() { return; } - if self.types.len() > 12 { + if self.fields.len() > 12 { self.generate_long_args(); return; } - let sig_parts: Vec<_> = repeat_n("&'static str", self.types.len()).collect(); + let sig_parts: Vec<_> = repeat_n("&'static str", self.fields.len()).collect(); let sig = sig_parts.join(", "); write_str!(self, " #[classattr]"); write_fmt!(self, " fn __match_args__() -> ({sig},) {{"); write_str!(self, " ("); - for variable_info in &self.types { - write_fmt!(self, " \"{}\",", variable_info.name); + for field_name in self.fields.keys() { + write_fmt!(self, " \"{field_name}\","); } write_str!(self, " )"); - write_str!(self, " }\n"); - write_str!(self, " #[classattr]"); - write_fmt!(self, " fn __slots__() -> ({sig},) {{"); - write_str!(self, " Self::__match_args__()"); - write_str!(self, " }\n"); - } - - fn generate_pack_method(&mut self) { - write_str!(self, " fn pack<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {"); - write_fmt!( - self, - " let flat_t = flat::{}::from_gil(py, self);", - &self.struct_t_name - ); - - if self.has_complex_pack { - write_str!(self, " let size = flat_t.get_size().next_power_of_two();"); - write_str!(self, ""); - write_str!(self, " let mut builder = FlatBufferBuilder::with_capacity(size);"); - write_str!(self, " let offset = flat_t.pack(&mut builder);"); - write_str!(self, " builder.finish(offset, None);"); - write_str!(self, ""); - write_str!(self, " PyBytes::new(py, builder.finished_data())"); - } else { - write_str!(self, " let item = flat_t.pack();"); - write_str!(self, ""); - write_str!(self, " PyBytes::new(py, &item.0)"); - } - - write_str!(self, " }"); - } - - fn generate_unpack_method(&mut self) { - write_str!(self, " #[staticmethod]"); - write_str!(self, " fn unpack(py: Python, data: &[u8]) -> PyResult> {"); - write_fmt!(self, " match root::(data) {{", self.struct_name); - write_str!( - self, - " Ok(flat_t) => Ok(crate::into_py_from(py, flat_t.unpack()))," - ); - write_str!(self, " Err(e) => Err(flat_err_to_py(e)),"); - write_str!(self, " }"); - write_str!(self, " }"); - } -} - -impl Generator for StructBindGenerator { - fn filename(&self) -> &str { - &self.filename - } - - fn struct_name(&self) -> &str { - &self.struct_name - } - - fn file_contents(&self) -> &Vec> { - &self.file_contents - } - - fn modify_source(&self, path: &Path) { - let mut contents = fs::read_to_string(path).unwrap(); - - #[cfg(windows)] - { - contents = contents.replace("\r\n", "\n"); - } - - contents = contents.replace("use self::flatbuffers", "use get_size::GetSize;\nuse self::flatbuffers"); - - contents = contents.replace("#[derive(Debug, Clone, PartialEq)]\n", "#[derive(PartialEq, GetSize)]\n"); - - contents = contents.replace( - "#[derive(Debug, Clone, PartialEq, Default)]\n", - "#[derive(PartialEq, Default, GetSize)]\n", - ); - - // delete unneeded auto-generated source code from flatc - let start = contents.find("impl core::fmt::Debug for ").unwrap(); - let end = contents[start..].find("\n}").unwrap() + 3; - contents.replace_range(start..start + end, ""); - - if let Some(start) = contents.find("impl<'a> Default for ") { - let end = contents[start..].find("\n}").unwrap() + 3; - contents.replace_range(start..start + end, ""); - } - - fs::write(path, contents).unwrap(); - } - - fn generate_definition(&mut self) { - let freelist_str = if self.freelist_size > 0 { - Cow::Owned(format!(", freelist = {}", self.freelist_size)) - } else { - Cow::Borrowed("") - }; - - let pyclass_start_str = "#[pyclass(module = \"rlbot_flatbuffers\", subclass, "; - if self.is_frozen { - write_fmt!(self, "{pyclass_start_str}get_all, frozen{freelist_str})]"); - } else if self.types.is_empty() { - write_fmt!(self, "{pyclass_start_str}frozen{freelist_str})]"); - } else { - write_fmt!(self, "{pyclass_start_str}get_all{freelist_str})]"); - } - - if self.types.is_empty() { - write_str!(self, "#[derive(Default)]"); - write_fmt!(self, "pub struct {} {{}}", self.struct_name); - write_str!(self, ""); - return; - } - - let gen_set = !(self.is_no_set || self.is_frozen); - - write_fmt!(self, "pub struct {} {{", self.struct_name); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - let mut add_set = true; - let variable_type = match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => String::from("Py"), - RustType::Vec(InnerVecType::String) => String::from("Vec"), - RustType::Vec(InnerVecType::Base(inner_type)) => format!("Vec<{}>", inner_type), - RustType::Vec(InnerVecType::Custom(_)) => String::from("Py"), - RustType::Box(inner_type) => format!("Py"), - RustType::Option(InnerOptionType::BaseType, inner_type) => { - format!("Option>") - } - RustType::Option(InnerOptionType::String, _) => String::from("Option>"), - RustType::Union(inner_type, true) | RustType::Option(_, inner_type) => { - format!("Option>") - } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => { - add_set = false; - String::from("Py") - } - _ => inner_type.clone(), - }, - RustType::String => String::from("Py"), - RustType::Union(inner_type, false) | RustType::Custom(inner_type) => { - format!("Py") - } - RustType::Other(inner_type) => format!("super::{inner_type}"), - }; - - if gen_set && add_set { - write_str!(self, " #[pyo3(set)]"); - } - - write_fmt!(self, " pub {variable_name}: {variable_type},"); - } - - write_str!(self, "}"); - write_str!(self, ""); - - write_fmt!(self, "impl crate::PyDefault for {} {{", self.struct_name); - write_str!(self, " fn py_default(py: Python) -> Py {"); - write_str!(self, " Py::new(py, Self {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if let Some((field, value)) = self.default_override { - if field == variable_name { - write_fmt!(self, " {variable_name}: {value},"); - continue; - } - } - - let end = match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => Cow::Borrowed("PyBytes::new(py, &[]).unbind()"), - RustType::Vec(InnerVecType::Custom(_)) => Cow::Borrowed("PyList::empty(py).unbind()"), - RustType::Vec(_) => Cow::Borrowed("Vec::new()"), - RustType::Union(_, true) | RustType::Option(_, _) => Cow::Borrowed("None"), - RustType::Union(inner_type, false) | RustType::Box(inner_type) | RustType::Custom(inner_type) => { - Cow::Owned(format!("super::{inner_type}::py_default(py)")) - } - RustType::Base(inner_type) => Cow::Borrowed(match inner_type.as_str() { - "f32" => "crate::pyfloat_default(py)", - _ => "Default::default()", - }), - RustType::String => Cow::Borrowed("crate::pydefault_string(py)"), - RustType::Other(_) => Cow::Borrowed("Default::default()"), - }; - - write_fmt!(self, " {variable_name}: {end},"); - } - - write_str!(self, " }).unwrap()"); write_str!(self, " }"); - write_str!(self, "}"); - write_str!(self, ""); - } - - fn generate_from_flat_impls(&mut self) { - let impl_type = format!("flat::{}", self.struct_t_name); - - if self.types.is_empty() { - write_fmt!(self, "impl From<{impl_type}> for {} {{", self.struct_name); - write_fmt!(self, " fn from(_: {impl_type}) -> Self {{"); - write_fmt!(self, " {} {{}}", self.struct_name); - write_str!(self, " }"); - write_str!(self, "}"); - write_str!(self, ""); - return; - } - - write_fmt!(self, "impl FromGil<{impl_type}> for {} {{", self.struct_name); - - write_str!(self, " #[allow(unused_variables)]"); - write_fmt!(self, " fn from_gil(py: Python, flat_t: {impl_type}) -> Self {{"); - write_fmt!(self, " {} {{", self.struct_name); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => { - write_fmt!( - self, - " {variable_name}: PyBytes::new(py, &flat_t.{variable_name}).unbind()," - ) - } - RustType::Vec(InnerVecType::String | InnerVecType::Base(_)) => { - write_fmt!(self, " {variable_name}: flat_t.{variable_name},") - } - RustType::Vec(InnerVecType::Custom(type_name)) => { - write_fmt!( - self, - " {variable_name}: PyList::new(py, flat_t.{variable_name}.into_iter().map(|x| crate::into_py_from::<_, super::{type_name}>(py, x))).unwrap().unbind(),", - ) - } - RustType::Option(InnerOptionType::Box, _) => { - write_fmt!( - self, - " {variable_name}: flat_t.{variable_name}.map(|x| crate::into_py_from(py, *x))," - ); - } - RustType::Option(InnerOptionType::String, _) => { - write_fmt!( - self, - " {variable_name}: flat_t.{variable_name}.map(|s| PyString::new(py, &s).unbind())," - ); - } - RustType::Option(_, _) => { - write_fmt!( - self, - " {variable_name}: flat_t.{variable_name}.map(|x| crate::into_py_from(py, x))," - ); - } - RustType::Box(_) => { - write_fmt!( - self, - " {variable_name}: crate::into_py_from(py, *flat_t.{variable_name}),", - ); - } - RustType::Union(inner_type, true) => { - write_fmt!(self, " {variable_name}: match flat_t.{variable_name} {{"); - write_fmt!(self, " flat::{inner_type}T::NONE => None,"); - write_str!(self, " x => Some(crate::into_py_from(py, x)),"); - write_str!(self, " },"); - } - RustType::Union(_, false) | RustType::Custom(_) => { - write_fmt!( - self, - " {variable_name}: crate::into_py_from(py, flat_t.{variable_name}),", - ); - } - RustType::String => { - write_fmt!( - self, - " {variable_name}: PyString::new(py, &flat_t.{variable_name}).unbind()," - ); - } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => { - write_fmt!( - self, - " {variable_name}: crate::float_to_py(py, flat_t.{variable_name})," - ); - } - _ => { - write_fmt!(self, " {variable_name}: flat_t.{variable_name},"); - } - }, - RustType::Other(_) => { - write_fmt!(self, " {variable_name}: flat_t.{variable_name}.into(),",); - } - } - } - - write_str!(self, " }"); - write_str!(self, " }"); - write_str!(self, "}"); - write_str!(self, ""); - } - - fn generate_to_flat_impls(&mut self) { - let impl_type = format!("flat::{}", self.struct_t_name); - - if self.types.is_empty() { - write_fmt!(self, "impl From<&{}> for {impl_type} {{", self.struct_name); - write_fmt!(self, " fn from(_: &{}) -> Self {{", self.struct_name); - write_str!(self, " Self {}"); - write_str!(self, " }"); - write_str!(self, "}"); - write_str!(self, ""); - return; - } - - write_fmt!(self, "impl FromGil<&{}> for {impl_type} {{", self.struct_name); - - write_str!(self, " #[allow(unused_variables)]"); - write_fmt!(self, " fn from_gil(py: Python, py_type: &{}) -> Self {{", self.struct_name); - write_str!(self, " Self {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - match &variable_info.rust_type { - RustType::Vec(InnerVecType::U8) => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.as_bytes(py).to_vec()," - ) - } - RustType::Vec(InnerVecType::String | InnerVecType::Base(_)) => { - write_fmt!(self, " {variable_name}: py_type.{variable_name}.clone(),") - } - RustType::Vec(InnerVecType::Custom(_)) => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.bind_borrowed(py).iter().map(|x| crate::from_pyany_into(py, x)).collect(),", - ) - } - RustType::Option(InnerOptionType::Box, _) => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.as_ref().map(|x| Box::new(crate::from_py_into(py, x)))," - ); - } - RustType::Option(InnerOptionType::String, _) => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.as_ref().map(|s| s.to_str(py).unwrap().to_string())," - ); - } - RustType::Option(_, _) => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.as_ref().map(|x| crate::from_py_into(py, x))," - ); - } - RustType::Box(_) => { - write_fmt!( - self, - " {variable_name}: Box::new(crate::from_py_into(py, &py_type.{variable_name})),", - ); - } - RustType::Union(inner_type, true) => { - write_fmt!(self, " {variable_name}: py_type"); - write_fmt!(self, " .{variable_name}"); - write_str!(self, " .as_ref()"); - write_fmt!(self, " .map_or(flat::{inner_type}T::NONE, |x| {{"); - write_fmt!( - self, - " crate::from_py_into::<_, flat::{inner_type}T>(py, x).into()" - ); - write_str!(self, " }),"); - } - RustType::Union(_, false) | RustType::Custom(_) => { - write_fmt!( - self, - " {variable_name}: crate::from_py_into(py, &py_type.{variable_name}),", - ); - } - RustType::String => { - write_fmt!( - self, - " {variable_name}: py_type.{variable_name}.to_str(py).unwrap().to_string(),", - ); - } - RustType::Base(inner_type) => match inner_type.as_str() { - "f32" => { - write_fmt!( - self, - " {variable_name}: crate::float_from_py(py, &py_type.{variable_name})," - ); - } - _ => { - write_fmt!(self, " {variable_name}: py_type.{variable_name},"); - } - }, - RustType::Other(_) => { - write_fmt!(self, " {variable_name}: (&py_type.{variable_name}).into(),",); - } - } - } - - write_str!(self, " }"); - write_str!(self, " }"); - write_str!(self, "}"); - write_str!(self, ""); } fn generate_py_methods(&mut self) { write_str!(self, "#[pymethods]"); - write_fmt!(self, "impl {} {{", self.struct_name); + write_fmt!(self, "impl {} {{", self.name); self.generate_new_method(); write_str!(self, ""); @@ -1128,11 +445,26 @@ impl Generator for StructBindGenerator { self.generate_args(); - self.generate_pack_method(); + write_str!(self, "}"); write_str!(self, ""); + } - self.generate_unpack_method(); - write_str!(self, "}"); + pub fn generate_binds(mut self) -> Vec> { + self.file_contents + .push(Cow::Borrowed(if self.fields.is_empty() { + "use crate::{FromGil, flat};" + } else { + "use crate::{FromGil, PyDefault, flat};" + })); + + write_str!(self, "use pyo3::{prelude::*, types::*};"); write_str!(self, ""); + + self.generate_definition(); + self.generate_from_flat_impls(); + self.generate_to_flat_impls(); + self.generate_py_methods(); + + self.file_contents } } diff --git a/codegen/table.rs b/codegen/table.rs new file mode 100644 index 0000000..93e260b --- /dev/null +++ b/codegen/table.rs @@ -0,0 +1,872 @@ +use crate::{FROZEN_TYPES, get_int_name}; +use indexmap::IndexMap; +use planus_types::{ + ast::IntegerType, + intermediate::{AbsolutePath, AssignMode, Declaration, SimpleType, TableField, TypeKind}, +}; +use std::{borrow::Cow, iter::repeat_n}; + +macro_rules! write_str { + ($self:ident, $s:expr) => { + $self.file_contents.push(Cow::Borrowed($s)) + }; +} + +macro_rules! write_fmt { + ($self:ident, $($arg:tt)*) => { + $self.file_contents.push(Cow::Owned(format!($($arg)*))) + }; +} + +pub struct TableBindGenerator<'a> { + name: &'a str, + fields: &'a IndexMap, + all_items: &'a IndexMap, + file_contents: Vec>, + is_frozen: bool, +} + +impl<'a> TableBindGenerator<'a> { + pub fn new( + name: &'a str, + fields: &'a IndexMap, + all_items: &'a IndexMap, + ) -> Self { + Self { + name, + fields, + all_items, + file_contents: Vec::new(), + is_frozen: FROZEN_TYPES.contains(&name), + } + } + + fn generate_definition(&mut self) { + let pyclass_start_str = "#[pyclass(module = \"rlbot_flatbuffers\", subclass, "; + if self.is_frozen { + write_fmt!(self, "{pyclass_start_str}frozen, get_all)]"); + } else if self.fields.is_empty() { + write_fmt!(self, "{pyclass_start_str}frozen)]"); + } else { + write_fmt!(self, "{pyclass_start_str}get_all)]"); + } + + if self.fields.is_empty() { + write_str!(self, "#[derive(Default)]"); + write_fmt!(self, "pub struct {} {{}}", self.name); + write_str!(self, ""); + return; + } + + write_fmt!(self, "pub struct {} {{", self.name); + + for (field_name, field_info) in self.fields { + let mut add_set = !self.is_frozen; + let variable_type = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => Cow::Borrowed("bool"), + SimpleType::Float(_) => { + add_set = false; + Cow::Borrowed("Py") + } + SimpleType::Integer(int_type) => Cow::Borrowed(get_int_name(int_type)), + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Py")) + } + SimpleType::Enum(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}")) + } + }, + TypeKind::String => Cow::Borrowed("Py"), + TypeKind::Table(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Py")) + } + TypeKind::Vector(inner_type) => match &inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + Cow::Borrowed("Py") + } + _ => Cow::Borrowed("Py"), + }, + TypeKind::Union(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + + Cow::Owned(format!("Py")) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + + if add_set { + write_str!(self, " #[pyo3(set)]"); + } + + match field_info.assign_mode { + AssignMode::Optional => { + write_fmt!(self, " pub {field_name}: Option<{variable_type}>,") + } + _ => write_fmt!(self, " pub {field_name}: {variable_type},"), + } + } + + write_str!(self, "}\n"); + write_fmt!(self, "impl crate::PyDefault for {} {{", self.name); + write_str!(self, " fn py_default(py: Python) -> Py {"); + write_str!(self, " Py::new(py, Self {"); + + for (field_name, field_info) in self.fields { + if matches!(field_info.assign_mode, AssignMode::Optional) { + write_fmt!(self, " {field_name}: None,"); + continue; + } + + let end = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Float(_) => Cow::Borrowed("crate::pyfloat_default(py)"), + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}::py_default(py)")) + } + _ => Cow::Borrowed("Default::default()"), + }, + TypeKind::String => Cow::Borrowed("crate::pydefault_string(py)"), + TypeKind::Table(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}::py_default(py)")) + } + TypeKind::Vector(inner_type) => match &inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + Cow::Borrowed("PyBytes::new(py, &[]).unbind()") + } + _ => Cow::Borrowed("PyList::empty(py).unbind()"), + }, + TypeKind::Union(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}::py_default(py)")) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + + write_fmt!(self, " {field_name}: {end},"); + } + + write_str!(self, " }).unwrap()"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + } + + fn generate_from_flat_impls(&mut self) { + let impl_type = format!("flat::{}", self.name); + + if self.fields.is_empty() { + write_fmt!(self, "impl From<{impl_type}> for {} {{", self.name); + write_fmt!(self, " fn from(_: {impl_type}) -> Self {{"); + write_fmt!(self, " {} {{}}", self.name); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + return; + } + + write_fmt!(self, "impl FromGil<{impl_type}> for {} {{", self.name); + + write_str!(self, " #[allow(unused_variables)]"); + write_fmt!( + self, + " fn from_gil(py: Python, flat_t: {impl_type}) -> Self {{" + ); + write_fmt!(self, " {} {{", self.name); + + for (field_name, field_info) in self.fields { + let end = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Float(_) => { + format!("crate::float_to_py(py, flat_t.{field_name})") + } + SimpleType::Bool | SimpleType::Integer(_) => { + format!("flat_t.{field_name}") + } + SimpleType::Struct(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!("flat_t.{field_name}.map(|x| crate::into_py_from(py, x))") + } + _ => { + format!("crate::into_py_from(py, flat_t.{field_name})") + } + }, + SimpleType::Enum(_) => { + format!("flat_t.{field_name}.into()") + } + }, + TypeKind::String => match field_info.assign_mode { + AssignMode::Optional => { + format!("flat_t.{field_name}.map(|s| PyString::new(py, &s).unbind())") + } + _ => { + format!("PyString::new(py, &flat_t.{field_name}).unbind()") + } + }, + TypeKind::Table(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!("flat_t.{field_name}.map(|x| crate::into_py_from(py, *x))") + } + _ => { + format!("crate::into_py_from(py, *flat_t.{field_name})") + } + }, + TypeKind::Vector(inner_type) => match &inner_type.kind { + TypeKind::String => { + format!("crate::into_pystringlist_from(py, flat_t.{field_name})") + } + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + format!("PyBytes::new(py, &flat_t.{field_name}).unbind()") + } + TypeKind::Table(idx) | TypeKind::SimpleType(SimpleType::Struct(idx)) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let type_name = path.0.last().unwrap(); + format!( + "PyList::new(py, flat_t.{field_name}.into_iter().map(|x| crate::into_py_from::<_, super::{type_name}>(py, x))).unwrap().unbind()" + ) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }, + TypeKind::Union(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!("flat_t.{field_name}.map(|x| crate::into_py_from(py, x))") + } + _ => { + format!("crate::into_py_from(py, flat_t.{field_name})") + } + }, + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + + write_fmt!(self, " {field_name}: {end},") + } + + write_str!(self, " }"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + } + + fn generate_to_flat_impls(&mut self) { + let impl_type = format!("flat::{}", self.name); + + if self.fields.is_empty() { + write_fmt!(self, "impl From<&{}> for {impl_type} {{", self.name); + write_fmt!(self, " fn from(_: &{}) -> Self {{", self.name); + write_str!(self, " Self {}"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + return; + } + + write_fmt!(self, "impl FromGil<&{}> for {impl_type} {{", self.name); + write_str!(self, " #[allow(unused_variables)]"); + write_fmt!( + self, + " fn from_gil(py: Python, py_type: &{}) -> Self {{", + self.name + ); + write_str!(self, " Self {"); + + for (field_name, field_info) in self.fields { + let end = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Float(_) => { + format!("crate::float_from_py(py, &py_type.{field_name})") + } + SimpleType::Bool | SimpleType::Integer(_) => { + format!("py_type.{field_name}") + } + SimpleType::Struct(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!( + "py_type.{field_name}.as_ref().map(|x| crate::from_py_into(py, x))" + ) + } + _ => { + format!("crate::from_py_into(py, &py_type.{field_name})",) + } + }, + SimpleType::Enum(_) => { + format!("py_type.{field_name}.into()") + } + }, + TypeKind::String => match field_info.assign_mode { + AssignMode::Optional => { + format!( + "py_type.{field_name}.as_ref().map(|s| s.to_str(py).unwrap().to_string())" + ) + } + _ => { + format!("py_type.{field_name}.to_str(py).unwrap().to_string()",) + } + }, + TypeKind::Table(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!( + "py_type.{field_name}.as_ref().map(|x| Box::new(crate::from_py_into(py, x)))" + ) + } + _ => { + format!("Box::new(crate::from_py_into(py, &py_type.{field_name}))",) + } + }, + TypeKind::Vector(inner_type) => match &inner_type.kind { + TypeKind::String => format!( + "py_type.{field_name}.bind_borrowed(py).iter().map(|x| crate::from_pystring_into(x)).collect()" + ), + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + format!("py_type.{field_name}.as_bytes(py).to_vec()") + } + TypeKind::Table(_) | TypeKind::SimpleType(SimpleType::Struct(_)) => { + format!( + "py_type.{field_name}.bind_borrowed(py).iter().map(|x| crate::from_pyany_into(py, x)).collect()" + ) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }, + TypeKind::Union(_) => match field_info.assign_mode { + AssignMode::Optional => { + format!("py_type.{field_name}.as_ref().map(|x| crate::from_py_into(py, x))") + } + _ => { + format!("crate::from_py_into(py, &py_type.{field_name})") + } + }, + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + + write_fmt!(self, " {field_name}: {end},",); + } + + write_str!(self, " }"); + write_str!(self, " }"); + write_str!(self, "}"); + write_str!(self, ""); + } + + fn generate_new_method(&mut self) { + write_str!(self, " #[new]"); + + if self.fields.is_empty() { + write_str!(self, " pub fn new() -> Self {"); + write_str!(self, " Self {}"); + write_str!(self, " }"); + return; + } + + let mut signature_parts = Vec::new(); + let mut needs_python = false; + + for (field_name, field_info) in self.fields { + if matches!(field_info.assign_mode, AssignMode::Optional) { + if let TypeKind::SimpleType(SimpleType::Struct(idx)) = &field_info.type_.kind { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + match name.as_str() { + "Bool" | "Float" => { + needs_python = true; + } + _ => {} + } + } + + signature_parts.push(format!("{field_name}=None")); + continue; + } + + signature_parts.push(match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => format!("{field_name}=false"), + SimpleType::Integer(_) => format!("{field_name}=0"), + SimpleType::Enum(_) => format!("{field_name}=Default::default()"), + SimpleType::Float(_) => { + needs_python = true; + format!("{field_name}=0.0") + } + SimpleType::Struct(_) => { + needs_python = true; + format!("{field_name}=None") + } + }, + _ => { + needs_python = true; + format!("{field_name}=None") + } + }); + } + + let max_num_types = if needs_python { 6 } else { 7 }; + if self.fields.len() > max_num_types { + write_str!(self, " #[allow(clippy::too_many_arguments)]"); + } + + write_fmt!( + self, + " #[pyo3(signature = ({}))]", + signature_parts.join(", ") + ); + write_str!(self, " pub fn new("); + + if needs_python { + write_str!(self, " py: Python,"); + } + + for (field_name, field_info) in self.fields { + let variable_type = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Bool => Cow::Borrowed("bool"), + SimpleType::Integer(int_type) => Cow::Borrowed(get_int_name(int_type)), + SimpleType::Float(_) => Cow::Borrowed("f64"), + SimpleType::Enum(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("super::{name}")) + } + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + match name.as_str() { + "Float" => Cow::Borrowed("Option"), + "Bool" => Cow::Borrowed("Option"), + _ => Cow::Owned(format!("Option>")), + } + } + }, + TypeKind::Table(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Option>")) + } + TypeKind::String => Cow::Borrowed("Option>"), + TypeKind::Vector(inner_type) => match inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + Cow::Borrowed("Option>") + } + _ => Cow::Borrowed("Option>"), + }, + TypeKind::Union(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!("Option")) + } + _ => todo!("Unknown field type for {field_name} in {}", self.name), + }; + + write_fmt!(self, " {field_name}: {variable_type},"); + } + + write_str!(self, " ) -> Self {"); + write_str!(self, " Self {"); + + for (field_name, field_info) in self.fields { + if matches!(field_info.assign_mode, AssignMode::Optional) { + match &field_info.type_.kind { + TypeKind::SimpleType(SimpleType::Struct(idx)) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + match name.as_str() { + "Bool" | "Float" => { + write_fmt!( + self, + " {field_name}: {field_name}.map(|x| x.into_gil(py))," + ); + } + _ => { + write_fmt!(self, " {field_name},"); + } + } + } + TypeKind::Union(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + write_fmt!( + self, + " {field_name}: {field_name}.map(|u| Py::new(py, super::{name}::new(u)).unwrap())," + ) + } + _ => { + write_fmt!(self, " {field_name},"); + } + } + continue; + } + + let end = match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!( + ": {field_name}.unwrap_or_else(|| super::{name}::py_default(py))" + )) + } + SimpleType::Float(_) => { + Cow::Owned(format!(": PyFloat::new(py, {field_name}).unbind()")) + } + _ => Cow::Borrowed(""), + }, + TypeKind::Table(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!( + ": {field_name}.unwrap_or_else(|| super::{name}::py_default(py))" + )) + } + TypeKind::String => Cow::Owned(format!( + ": {field_name}.unwrap_or_else(|| crate::pydefault_string(py))" + )), + TypeKind::Vector(inner_type) => match inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => Cow::Owned( + format!(": {field_name}.unwrap_or_else(|| PyBytes::new(py, &[]).unbind())"), + ), + _ => Cow::Owned(format!( + ": {field_name}.unwrap_or_else(|| PyList::empty(py).unbind())" + )), + }, + TypeKind::Union(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + Cow::Owned(format!( + ": {field_name}.map(|u| Py::new(py, super::{name}::new(u)).unwrap()).unwrap_or_else(|| super::{name}::py_default(py))" + )) + } + _ => Cow::Borrowed(""), + }; + + write_fmt!(self, " {field_name}{end},"); + } + + write_str!(self, " }"); + write_str!(self, " }"); + + if self.is_frozen { + return; + } + + for (field_name, field_info) in self.fields { + match &field_info.type_.kind { + TypeKind::SimpleType(SimpleType::Float(_)) => { + write_str!(self, "\n #[setter]"); + write_fmt!( + self, + " pub fn {field_name}(&mut self, py: Python, value: f64) {{", + ); + write_fmt!( + self, + " self.{field_name} = PyFloat::new(py, value).unbind();" + ); + write_str!(self, " }"); + } + _ => continue, + } + } + } + + fn generate_str_method(&mut self) { + write_str!(self, " pub fn __str__(&self, py: Python) -> String {"); + write_str!(self, " self.__repr__(py)"); + write_str!(self, " }"); + } + + fn generate_repr_method(&mut self) { + if self.fields.is_empty() { + write_str!(self, " pub fn __repr__(&self, _py: Python) -> String {"); + write_fmt!(self, " String::from(\"{}()\")", self.name); + write_str!(self, " }"); + return; + } + + write_str!(self, " #[allow(unused_variables)]"); + write_str!(self, " pub fn __repr__(&self, py: Python) -> String {"); + write_str!(self, " format!("); + + let repr_signature = self + .fields + .iter() + .map(|(field_name, field_info)| match &field_info.type_.kind { + TypeKind::String => format!("{field_name}={{:?}}"), + TypeKind::Vector(inner_type) => match inner_type.kind { + TypeKind::SimpleType(SimpleType::Integer(IntegerType::U8)) => { + format!("{field_name}=bytes([{{}}])") + } + _ => format!("{field_name}=[{{}}]"), + }, + _ => format!("{field_name}={{}}"), + }) + .collect::>() + .join(", "); + write_fmt!(self, " \"{}({repr_signature})\",", self.name); + + for (field_name, field_info) in self.fields { + match &field_info.type_.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Struct(_) => match field_info.assign_mode { + AssignMode::Optional => { + write_fmt!(self, " self.{field_name}"); + write_str!(self, " .as_ref()"); + write_str!(self, " .map_or_else(crate::none_str, |x| {"); + write_str!(self, " x.borrow(py).__repr__(py)"); + write_str!(self, " }),"); + } + _ => { + write_fmt!( + self, + " self.{field_name}.borrow(py).__repr__(py)," + ); + } + }, + SimpleType::Bool => { + write_fmt!(self, " crate::bool_to_str(self.{field_name}),"); + } + SimpleType::Integer(_) | SimpleType::Float(_) => { + write_fmt!(self, " self.{field_name},"); + } + SimpleType::Enum(_) => { + write_fmt!(self, " self.{field_name}.__repr__(),") + } + }, + TypeKind::String => match field_info.assign_mode { + AssignMode::Optional => { + write_fmt!(self, " self.{field_name}"); + write_str!(self, " .as_ref()"); + write_str!(self, " .map_or_else(crate::none_str, |i| {"); + write_str!( + self, + " format!(\"{:?}\", i.to_str(py).unwrap().to_string())" + ); + write_str!(self, " }),"); + } + _ => { + write_fmt!( + self, + " self.{field_name}.bind(py).to_cow().unwrap()," + ); + } + }, + TypeKind::Table(_) => match field_info.assign_mode { + AssignMode::Optional => { + write_fmt!(self, " self.{field_name}"); + write_str!(self, " .as_ref()"); + write_str!(self, " .map_or_else(crate::none_str, |x| {"); + write_str!(self, " x.borrow(py).__repr__(py)"); + write_str!(self, " }),"); + } + _ => { + write_fmt!( + self, + " self.{field_name}.borrow(py).__repr__(py)," + ); + } + }, + TypeKind::Union(_) => match field_info.assign_mode { + AssignMode::Optional => { + write_fmt!(self, " self.{field_name}"); + write_str!(self, " .as_ref()"); + write_str!( + self, + " .map_or_else(crate::none_str, |i| i.borrow(py).inner_repr(py))," + ); + } + _ => { + write_fmt!( + self, + " self.{field_name}.borrow(py).inner_repr(py)," + ); + } + }, + TypeKind::Vector(inner_type) => { + write_fmt!(self, " self.{field_name}"); + + match inner_type.kind { + TypeKind::SimpleType(simple_type) => match simple_type { + SimpleType::Integer(IntegerType::U8) => { + write_str!(self, " .as_bytes(py)"); + write_str!(self, " .iter()"); + write_str!(self, " .map(ToString::to_string)"); + } + SimpleType::Struct(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + write_str!(self, " .bind_borrowed(py)"); + write_str!(self, " .iter()"); + write_fmt!( + self, + " .map(|x| x.downcast_into::().unwrap().borrow().__repr__(py))" + ); + } + _ => { + write_str!(self, " .iter()"); + write_str!(self, " .map(ToString::to_string)"); + } + }, + TypeKind::String => { + write_str!(self, " .bind_borrowed(py)"); + write_str!(self, " .iter()"); + write_str!( + self, + " .map(|s| format!(\"{:?}\", crate::from_pystring_into(s)))" + ); + } + TypeKind::Table(idx) => { + let (path, _) = self.all_items.get_index(idx.0).unwrap(); + let name = path.0.last().unwrap(); + write_str!(self, " .bind_borrowed(py)"); + write_str!(self, " .iter()"); + write_fmt!( + self, + " .map(|x| x.downcast_into::().unwrap().borrow().__repr__(py))" + ); + } + _ => continue, + } + + write_str!(self, " .collect::>()"); + write_str!(self, " .join(\", \"),"); + } + _ => write_fmt!(self, " self.{field_name}.__repr__(),"), + } + } + + write_str!(self, " )"); + write_str!(self, " }"); + } + + fn generate_long_args(&mut self) { + write_str!(self, " #[classattr]"); + write_str!( + self, + " fn __match_args__(py: Python) -> Bound {" + ); + write_str!(self, " pyo3::types::PyTuple::new(py, ["); + + for field_name in self.fields.keys() { + write_fmt!(self, " \"{field_name}\","); + } + + write_str!(self, " ]).unwrap()"); + write_str!(self, " }\n"); + } + + fn generate_args(&mut self) { + if self.fields.is_empty() { + return; + } + + if self.fields.len() > 12 { + self.generate_long_args(); + return; + } + + let sig_parts: Vec<_> = repeat_n("&'static str", self.fields.len()).collect(); + let sig = sig_parts.join(", "); + + write_str!(self, " #[classattr]"); + write_fmt!(self, " fn __match_args__() -> ({sig},) {{"); + write_str!(self, " ("); + + for field_name in self.fields.keys() { + write_fmt!(self, " \"{field_name}\","); + } + + write_str!(self, " )"); + write_str!(self, " }"); + } + + fn generate_pack_method(&mut self) { + write_str!( + self, + " fn pack<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {" + ); + write_str!( + self, + " let mut builder = Builder::with_capacity(u16::MAX as usize);\n" + ); + write_fmt!( + self, + " let flat_t = flat::{}::from_gil(py, self);", + self.name + ); + write_str!( + self, + " PyBytes::new(py, builder.finish(flat_t, None))" + ); + write_str!(self, " }"); + } + + fn generate_unpack_method(&mut self) { + write_str!(self, " #[staticmethod]"); + write_str!( + self, + " fn unpack(py: Python, data: &[u8]) -> PyResult> {" + ); + write_fmt!( + self, + " let flat_t_ref = flat::{}Ref::read_as_root(data).map_err(flat_err_to_py)?;", + self.name + ); + write_fmt!( + self, + " let flat_t = flat::{}::try_from(flat_t_ref).map_err(flat_err_to_py)?;\n", + self.name + ); + write_str!(self, " Ok(crate::into_py_from(py, flat_t))"); + write_str!(self, " }"); + } + + fn generate_py_methods(&mut self) { + write_str!(self, "#[pymethods]"); + write_fmt!(self, "impl {} {{", self.name); + + self.generate_new_method(); + write_str!(self, ""); + + self.generate_str_method(); + write_str!(self, ""); + + self.generate_repr_method(); + write_str!(self, ""); + + self.generate_args(); + + self.generate_pack_method(); + write_str!(self, ""); + + self.generate_unpack_method(); + write_str!(self, "}"); + write_str!(self, ""); + } + + pub fn generate_binds(mut self) -> Vec> { + self.file_contents + .push(Cow::Borrowed(if self.fields.is_empty() { + "use crate::{FromGil, flat_err_to_py, flat};" + } else { + "use crate::{FromGil, IntoGil, PyDefault, flat, flat_err_to_py};" + })); + + write_str!(self, "use planus::{Builder, ReadAsRoot};"); + write_str!(self, "use pyo3::{prelude::*, types::*};"); + write_str!(self, ""); + + self.generate_definition(); + self.generate_from_flat_impls(); + self.generate_to_flat_impls(); + self.generate_py_methods(); + + self.file_contents + } +} diff --git a/codegen/unions.rs b/codegen/unions.rs index 4191c75..c701a9f 100644 --- a/codegen/unions.rs +++ b/codegen/unions.rs @@ -1,15 +1,8 @@ -use crate::{PythonBindType, enums::CustomEnumType, generator::Generator}; -use std::{borrow::Cow, fs, path::Path}; - -pub struct UnionBindGenerator { - pub filename: String, - pub struct_name: String, - struct_t_name: String, - pub types: Vec, - file_contents: Vec>, - is_frozen: bool, - is_no_set: bool, -} +use indexmap::IndexMap; +use planus_types::intermediate::UnionVariant; +use std::borrow::Cow; + +pub const NO_SET_TYPES: [&str; 1] = ["PlayerClass"]; macro_rules! write_str { ($self:ident, $s:expr) => { @@ -23,183 +16,54 @@ macro_rules! write_fmt { }; } -impl UnionBindGenerator { - pub fn new(filename: String, struct_name: String, struct_t_name: String, types: Vec) -> Option { - let is_frozen = PythonBindType::FROZEN_TYPES.contains(&struct_name.as_str()); - let is_no_set = PythonBindType::NO_SET_TYPES.contains(&struct_name.as_str()); - - let file_contents = vec![ - Cow::Borrowed("use crate::{FromGil, generated::rlbot::flat};"), - Cow::Borrowed("use pyo3::{Bound, Py, PyAny, Python, pyclass, pymethods};"), - Cow::Borrowed(""), - ]; - - Some(Self { - filename: filename.to_string(), - struct_name, - struct_t_name, - types, - file_contents, - is_frozen, - is_no_set, - }) - } - - fn generate_new_method(&mut self) { - assert!(u8::try_from(self.types.len()).is_ok()); - - write_str!(self, " #[new]"); - write_fmt!(self, " pub fn new(item: {}Union) -> Self {{", self.struct_name); - write_str!(self, " Self { item }"); - write_str!(self, " }"); - write_str!(self, ""); - write_str!(self, " #[getter(item)]"); - - write_str!(self, " pub fn get(&self, py: Python) -> Option> {"); - write_str!(self, " match &self.item {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if variable_name != "NONE" { - write_fmt!( - self, - " {}Union::{variable_name}(item) => Some(item.clone_ref(py).into_any()),", - self.struct_name - ); - } - } - - write_str!(self, " }"); - write_str!(self, " }"); - } - - fn generate_str_method(&mut self) { - write_str!(self, " pub fn __str__(&self, py: Python) -> String {"); - write_str!(self, " self.__repr__(py)"); - write_str!(self, " }"); - } - - fn generate_inner_repr_method(&mut self) { - write_str!(self, " pub fn inner_repr(&self, py: Python) -> String {"); - write_str!(self, " match &self.item {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if variable_info.value.is_some() { - write_fmt!( - self, - " {}Union::{variable_name}(item) => item.borrow(py).__repr__(py),", - self.struct_name - ); - } - } - - write_str!(self, " }"); - write_str!(self, " }"); - } - - fn generate_repr_method(&mut self) { - write_str!(self, " pub fn __repr__(&self, py: Python) -> String {"); - write_str!(self, " match &self.item {"); - - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if variable_info.value.is_some() { - write_fmt!( - self, - " {}Union::{variable_name}(item) => format!(\"{}({{}})\", item.borrow(py).__repr__(py)),", - self.struct_name, - self.struct_name - ); - } - } - - write_str!(self, " }"); - write_str!(self, " }"); - } +pub struct UnionBindGenerator<'a> { + name: &'a str, + variants: &'a IndexMap, + file_contents: Vec>, + is_no_set: bool, } -impl Generator for UnionBindGenerator { - fn filename(&self) -> &str { - &self.filename - } - - fn struct_name(&self) -> &str { - &self.struct_name - } - - fn file_contents(&self) -> &Vec> { - &self.file_contents - } - - fn modify_source(&self, path: &Path) { - let mut contents = fs::read_to_string(path).unwrap(); - - #[cfg(windows)] - { - contents = contents.replace("\r\n", "\n"); +impl<'a> UnionBindGenerator<'a> { + pub fn new(name: &'a str, variants: &'a IndexMap) -> Self { + Self { + name, + variants, + file_contents: Vec::new(), + is_no_set: NO_SET_TYPES.contains(&name), } - - contents = contents.replace("use self::flatbuffers", "use get_size::GetSize;\nuse self::flatbuffers"); - - contents = contents.replace("#[derive(Debug, Clone, PartialEq)]\n", "#[derive(PartialEq, GetSize)]\n"); - - // delete unneeded auto-generated source code from flatc - let start = contents.find("impl core::fmt::Debug for ").unwrap(); - let end = contents[start..].find("\n}").unwrap() + 2; - contents.replace_range(start..start + end, ""); - - for _ in 0..3 { - let start = contents.find("#[deprecated").unwrap(); - let end = contents[start..].find(";\n").unwrap() + 2; - contents.replace_range(start..start + end, ""); - } - - let start = contents.find(" /// Returns the variant's name").unwrap(); - let end = contents[start..].find("\n }\n").unwrap() + 5; - contents.replace_range(start..start + end, ""); - - fs::write(path, contents).unwrap(); } fn generate_definition(&mut self) { write_fmt!(self, "#[derive(pyo3::FromPyObject)]"); - write_fmt!(self, "pub enum {}Union {{", self.struct_name); - - for variable_info in self.types.iter().skip(1) { - let variable_name = variable_info.name.as_str(); + write_fmt!(self, "pub enum {}Union {{", self.name); - write_fmt!(self, " {variable_name}(Py),"); + for var_name in self.variants.keys() { + write_fmt!(self, " {var_name}(Py),"); } write_str!(self, "}"); write_str!(self, ""); - if self.is_frozen { - write_str!(self, "#[pyclass(module = \"rlbot_flatbuffers\", frozen)]"); - } else if self.is_no_set { + if self.is_no_set { write_str!(self, "#[pyclass(module = \"rlbot_flatbuffers\")]"); } else { write_str!(self, "#[pyclass(module = \"rlbot_flatbuffers\", set_all)]"); } - write_fmt!(self, "pub struct {} {{", self.struct_name); - write_fmt!(self, " item: {}Union,", self.struct_name); + write_fmt!(self, "pub struct {} {{", self.name); + write_fmt!(self, " item: {}Union,", self.name); write_str!(self, "}"); write_str!(self, ""); - write_fmt!(self, "impl crate::PyDefault for {} {{", self.struct_name); + write_fmt!(self, "impl crate::PyDefault for {} {{", self.name); write_str!(self, " fn py_default(py: Python) -> Py {"); write_str!(self, " Py::new(py, Self {"); + + let first_var_name = self.variants.keys().next().unwrap(); write_fmt!( self, - " item: {}Union::{}(super::{}::py_default(py)),", - self.struct_name, - self.types[1].name, - self.types[1].name + " item: {}Union::{first_var_name}(super::{first_var_name}::py_default(py)),", + self.name ); write_str!(self, " }).unwrap()"); write_str!(self, " }"); @@ -208,38 +72,41 @@ impl Generator for UnionBindGenerator { } fn generate_from_flat_impls(&mut self) { - write_fmt!(self, "impl FromGil for {} {{", self.struct_t_name, self.struct_name); + write_fmt!( + self, + "impl FromGil for {} {{", + self.name, + self.name + ); write_fmt!( self, " fn from_gil(py: Python, flat_t: flat::{}) -> Self {{", - self.struct_t_name + self.name ); write_str!(self, " match flat_t {"); - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if variable_name == "NONE" { - write_fmt!(self, " flat::{}::NONE => unreachable!(),", self.struct_t_name,); - } else { - write_fmt!( - self, - " flat::{}::{variable_name}(item) => {} {{", - self.struct_t_name, - self.struct_name, - ); - - write_fmt!(self, " item: {}Union::{variable_name}(", self.struct_name); - - write_fmt!( - self, - " Py::new(py, super::{variable_name}::from_gil(py, *item)).unwrap()," - ); - - write_fmt!(self, " ),"); - write_fmt!(self, " }},"); - } + for var_name in self.variants.keys() { + write_fmt!( + self, + " flat::{}::{var_name}(item) => {} {{", + self.name, + self.name, + ); + + write_fmt!( + self, + " item: {}Union::{var_name}(", + self.name + ); + + write_fmt!( + self, + " Py::new(py, super::{var_name}::from_gil(py, *item)).unwrap()," + ); + + write_fmt!(self, " ),"); + write_fmt!(self, " }},"); } write_str!(self, " }"); @@ -252,27 +119,31 @@ impl Generator for UnionBindGenerator { write_fmt!( self, "impl FromGil<&{}> for flat::{} {{", - self.struct_name, - self.struct_t_name + self.name, + self.name + ); + write_fmt!( + self, + " fn from_gil(py: Python, py_type: &{}) -> Self {{", + self.name ); - write_fmt!(self, " fn from_gil(py: Python, py_type: &{}) -> Self {{", self.struct_name); write_str!(self, " match &py_type.item {"); - for variable_info in &self.types { - let variable_name = variable_info.name.as_str(); - - if let Some(ref value) = variable_info.value { - write_fmt!(self, " {}Union::{value}(item) => {{", self.struct_name,); + for var_name in self.variants.keys() { + write_fmt!( + self, + " {}Union::{var_name}(item) => {{", + self.name, + ); - write_fmt!( - self, - " flat::{}::{variable_name}(Box::new(crate::from_py_into(py, item)))", - self.struct_t_name - ); + write_fmt!( + self, + " flat::{}::{var_name}(Box::new(crate::from_py_into(py, item)))", + self.name + ); - write_str!(self, " },"); - } + write_str!(self, " },"); } write_str!(self, " }"); @@ -281,9 +152,76 @@ impl Generator for UnionBindGenerator { write_str!(self, ""); } + fn generate_new_method(&mut self) { + assert!(u8::try_from(self.variants.len()).is_ok()); + + write_str!(self, " #[new]"); + write_fmt!(self, " pub fn new(item: {}Union) -> Self {{", self.name); + write_str!(self, " Self { item }"); + write_str!(self, " }"); + write_str!(self, ""); + write_str!(self, " #[getter(item)]"); + + write_str!( + self, + " pub fn get(&self, py: Python) -> Option> {" + ); + write_str!(self, " match &self.item {"); + + for var_name in self.variants.keys() { + write_fmt!( + self, + " {}Union::{var_name}(item) => Some(item.clone_ref(py).into_any()),", + self.name + ); + } + + write_str!(self, " }"); + write_str!(self, " }"); + } + + fn generate_str_method(&mut self) { + write_str!(self, " pub fn __str__(&self, py: Python) -> String {"); + write_str!(self, " self.__repr__(py)"); + write_str!(self, " }"); + } + + fn generate_inner_repr_method(&mut self) { + write_str!(self, " pub fn inner_repr(&self, py: Python) -> String {"); + write_str!(self, " match &self.item {"); + + for var_name in self.variants.keys() { + write_fmt!( + self, + " {}Union::{var_name}(item) => item.borrow(py).__repr__(py),", + self.name + ); + } + + write_str!(self, " }"); + write_str!(self, " }"); + } + + fn generate_repr_method(&mut self) { + write_str!(self, " pub fn __repr__(&self, py: Python) -> String {"); + write_str!(self, " match &self.item {"); + + for var_name in self.variants.keys() { + write_fmt!( + self, + " {}Union::{var_name}(item) => format!(\"{}({{}})\", item.borrow(py).__repr__(py)),", + self.name, + self.name + ); + } + + write_str!(self, " }"); + write_str!(self, " }"); + } + fn generate_py_methods(&mut self) { write_str!(self, "#[pymethods]"); - write_fmt!(self, "impl {} {{", self.struct_name); + write_fmt!(self, "impl {} {{", self.name); self.generate_new_method(); write_str!(self, ""); @@ -298,4 +236,17 @@ impl Generator for UnionBindGenerator { write_str!(self, "}"); write_str!(self, ""); } + + pub fn generate_binds(mut self) -> Vec> { + write_str!(self, "use crate::{FromGil, PyDefault, flat};"); + write_str!(self, "use pyo3::prelude::*;"); + write_str!(self, ""); + + self.generate_definition(); + self.generate_from_flat_impls(); + self.generate_to_flat_impls(); + self.generate_py_methods(); + + self.file_contents + } } diff --git a/rustfmt.toml b/rustfmt.toml index 1c8097d..65a8834 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,2 @@ use_field_init_shorthand = true -use_try_shorthand = true -max_width = 125 \ No newline at end of file +use_try_shorthand = true \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 126c69e..859b6ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,23 @@ -#[allow( - clippy::extra_unused_lifetimes, - clippy::missing_safety_doc, - clippy::derivable_impls, - clippy::unnecessary_cast, - clippy::size_of_in_element_count, - clippy::needless_lifetimes, - clippy::too_long_first_doc_paragraph, - unsafe_op_in_unsafe_fn, - non_snake_case, - unused_imports -)] -pub mod generated; +mod planus_flat; +pub use planus_flat::rlbot::flat; -#[allow(clippy::enum_variant_names, clippy::useless_conversion, unused_imports)] +#[allow(clippy::enum_variant_names, unused_imports)] mod python; use pyo3::{PyClass, create_exception, exceptions::PyValueError, prelude::*, types::*}; use python::*; use std::{panic::Location, path::MAIN_SEPARATOR}; -create_exception!(rlbot_flatbuffers, InvalidFlatbuffer, PyValueError, "Invalid FlatBuffer"); +create_exception!( + rlbot_flatbuffers, + InvalidFlatbuffer, + PyValueError, + "Invalid FlatBuffer" +); #[inline(never)] #[track_caller] -pub fn flat_err_to_py(err: flatbuffers::InvalidFlatbuffer) -> PyErr { +pub fn flat_err_to_py(err: planus::Error) -> PyErr { let caller = Location::caller(); let err_msg = format!( "Can't make flatbuffer @ \"rlbot_flatbuffers{MAIN_SEPARATOR}{}\":\n {err}", @@ -82,6 +76,20 @@ where (&*obj.downcast_into::().unwrap().borrow()).into_gil(py) } +fn into_pystringlist_from(py: Python, obj: Vec) -> Py { + PyList::new(py, obj.into_iter().map(|x| PyString::new(py, &x).unbind())) + .unwrap() + .unbind() +} + +fn from_pystring_into(obj: Bound) -> String { + obj.downcast_into::() + .unwrap() + .to_str() + .unwrap() + .to_string() +} + pub trait PyDefault: Sized + PyClass { fn py_default(py: Python) -> Py; } @@ -134,14 +142,8 @@ pub enum PartFloats { impl FromGil for Py { fn from_gil(py: Python, floats: PartFloats) -> Self { match floats { - PartFloats::Float(num) => Py::new( - py, - Float { - val: PyFloat::new(py, num).unbind(), - }, - ) - .unwrap(), PartFloats::Flat(float) => float, + PartFloats::Float(num) => Py::new(py, Float::new(py, num)).unwrap(), } } }