From ce4306223802388ea95b39e5e460728fa138f871 Mon Sep 17 00:00:00 2001 From: Niklas Dewally Date: Thu, 19 Sep 2024 14:42:37 +0100 Subject: [PATCH] uniplate_derive: add new Uniplate derive macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reimplement the uniplate-derive macro to allow for Biplates and "multiple type traversals" as discussed in [Niklas Dewally, 2024‐03: Implementing Uniplates and Biplates with Structure Preserving Trees]. (currently at https://github.com/conjure-cp/conjure-oxide/wiki/2024%E2%80%9003-Implementing-Uniplates-and-Biplates-with-Structure-Preserving-Trees). Implementation overview ======================= The library syn supports the creation of custom AST nodes. Using the Parse trait, one can implement parsers for these custom nodes by combining existing Rust AST nodes together as well as parsing Rust tokens by hand. The macro code has two parts: parsing into a custom AST, and using this AST for code-gen. This simplifies code-gen, as only the data needed for Uniplate is encoded in the AST. This also allows for better error reporting, as Most errors are now reported during parsing, allowing errors to be associated with the specific tokens that caused them. The biggest change in Uniplate's AST compared to Rust's is the representation of types. Uniplate has three kinds of type: boxed pointer types, plateable types, and unpalatable types. Plateable types include the current type being derived, or other types explicitly specified through the walk_through attribute in the macro invocation. Boxed pointer types represent smart pointers such as Box and Rc. These use the .borrow() function instead of & to pass the inner value to the Biplate instance. Generating instances for Box and Rc would mean that values are constantly moved from stack to heap - this avoids this. Implementations for standard library iterator types and primitives are provided by Uniplate. These implementations are generated by declarative macro, allowing easy extension to more collection types in the future. Testing ======= Add trybuild, a library for UI testing derive macros. The Rust tester does not run at-all when test code does not compile, but we need to test whether our macro gives a compile error when it should. trybuild also checks that the error messages given by the compiler have not changed. Two test ASTs are used for Uniplate and Biplates: a toy statement-expression found in the original paper, and the current Conjure Oxide AST. Property-based testing in paper.rs shows that children() and with_children() work for random homogenous AST's, including with boxes and iterators. As these two methods essentially act as wrappers for the child tree and context function returned by the uniplate() / biplate() methods, this test is sufficient to show that automatically derived Uniplate implementations act as intended. Signed-off-by: Niklas Dewally --- Cargo.lock | 798 ++++++++++-------- crates/uniplate/.gitignore | 1 + crates/uniplate/Cargo.toml | 8 + crates/uniplate/examples/stmt.rs | 41 + .../test_common/paper.txt | 1 + crates/uniplate/src/biplate.rs | 2 +- crates/uniplate/src/impls.rs | 39 + crates/uniplate/src/lib.rs | 388 +++++++-- crates/uniplate/src/test_common/paper.rs | 53 +- .../derive-fail/no-uniplate-attr.rs.disabled | 17 + .../tests/derive-fail/no-uniplate-attr.stderr | 5 + .../allow-enum-fields-with-trailing-commas.rs | 17 + .../allow-enum-variant-with-no-fields.rs | 8 + .../allow-enums-with-trailing-commas.rs | 18 + .../derive-pass/allow-nested-vec2.rs.ignore | 9 + .../tests/derive-pass/biplate-stmt.rs | 43 + .../uniplate/tests/derive-pass/conjure_ast.rs | 109 +++ crates/uniplate/tests/derive_test_runner.rs | 7 + crates/uniplate/tests/stmt.rs | 1 + crates/uniplate_derive/Cargo.toml | 13 +- crates/uniplate_derive/src/ast/data.rs | 148 ++++ .../uniplate_derive/src/ast/derive_input.rs | 173 ++++ crates/uniplate_derive/src/ast/mod.rs | 7 + crates/uniplate_derive/src/ast/typ.rs | 259 ++++++ crates/uniplate_derive/src/lib.rs | 368 ++++---- crates/uniplate_derive/src/prelude.rs | 17 + crates/uniplate_derive/src/state.rs | 91 ++ crates/uniplate_derive/src/utils/generate.rs | 185 ---- crates/uniplate_derive/src/utils/mod.rs | 2 - crates/uniplate_derive/src/utils/parse.rs | 133 --- crates/uniplate_derive/tests/macro_tests.rs | 244 ------ 31 files changed, 2033 insertions(+), 1172 deletions(-) create mode 100644 crates/uniplate/.gitignore create mode 100644 crates/uniplate/examples/stmt.rs create mode 100644 crates/uniplate/proptest-regressions/test_common/paper.txt create mode 100644 crates/uniplate/src/impls.rs create mode 100644 crates/uniplate/tests/derive-fail/no-uniplate-attr.rs.disabled create mode 100644 crates/uniplate/tests/derive-fail/no-uniplate-attr.stderr create mode 100644 crates/uniplate/tests/derive-pass/allow-enum-fields-with-trailing-commas.rs create mode 100644 crates/uniplate/tests/derive-pass/allow-enum-variant-with-no-fields.rs create mode 100644 crates/uniplate/tests/derive-pass/allow-enums-with-trailing-commas.rs create mode 100644 crates/uniplate/tests/derive-pass/allow-nested-vec2.rs.ignore create mode 100644 crates/uniplate/tests/derive-pass/biplate-stmt.rs create mode 100644 crates/uniplate/tests/derive-pass/conjure_ast.rs create mode 100644 crates/uniplate/tests/derive_test_runner.rs create mode 100644 crates/uniplate/tests/stmt.rs create mode 100644 crates/uniplate_derive/src/ast/data.rs create mode 100644 crates/uniplate_derive/src/ast/derive_input.rs create mode 100644 crates/uniplate_derive/src/ast/mod.rs create mode 100644 crates/uniplate_derive/src/ast/typ.rs create mode 100644 crates/uniplate_derive/src/prelude.rs create mode 100644 crates/uniplate_derive/src/state.rs delete mode 100644 crates/uniplate_derive/src/utils/generate.rs delete mode 100644 crates/uniplate_derive/src/utils/mod.rs delete mode 100644 crates/uniplate_derive/src/utils/parse.rs delete mode 100644 crates/uniplate_derive/tests/macro_tests.rs diff --git a/Cargo.lock b/Cargo.lock index e72baf8015..3292d16d9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -43,77 +43,78 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -128,7 +129,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags", "cexpr", "clang-sys", "itertools 0.12.1", @@ -137,14 +138,34 @@ dependencies = [ "log", "prettyplease", "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "regex", "rustc-hash", "shlex", - "syn 2.0.65", + "syn 2.0.77", "which", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote 1.0.37", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -162,15 +183,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmaps" @@ -183,25 +198,31 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.0.104" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -221,30 +242,30 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] name = "chuffed_rs" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cc", ] [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -253,9 +274,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.8" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -263,39 +284,39 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "conjure_core" @@ -332,8 +353,8 @@ version = "0.1.0" dependencies = [ "linkme", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -363,15 +384,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -379,27 +400,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.36", - "strsim 0.10.0", - "syn 2.0.65", + "quote 1.0.37", + "strsim", + "syn 2.0.77", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -419,7 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -430,7 +451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302ccf094df1151173bb6f5a2282fcd2f45accd5eae1bdf82dcbfefbc501ad5c" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -453,17 +474,17 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum_compatability_macro" version = "0.1.0" dependencies = [ "itertools 0.13.0", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -474,28 +495,29 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fnv" @@ -505,9 +527,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -516,9 +538,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -534,9 +556,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -565,14 +587,14 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -625,15 +647,21 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.12.1" @@ -660,26 +688,26 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "kissat-rs" -version = "0.1.2" -source = "git+https://github.com/firefighterduck/kissat-rs?branch=main#f46851317ba316c3d9fa1c34ca4c3261f89e9647" +version = "0.1.3" +source = "git+https://github.com/firefighterduck/kissat-rs?branch=main#45cc75e369c8215c826647f100f4fd0eee3198e9" dependencies = [ "derive-try-from-primitive", "kissat-sys", @@ -688,10 +716,10 @@ dependencies = [ [[package]] name = "kissat-sys" -version = "0.1.2" -source = "git+https://github.com/firefighterduck/kissat-rs?branch=main#f46851317ba316c3d9fa1c34ca4c3261f89e9647" +version = "0.1.3" +source = "git+https://github.com/firefighterduck/kissat-rs?branch=main#45cc75e369c8215c826647f100f4fd0eee3198e9" dependencies = [ - "bindgen", + "bindgen 0.70.1", ] [[package]] @@ -704,9 +732,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -716,18 +744,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -738,35 +766,35 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linkme" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb76662d78edc9f9bf56360d6919bdacc8b7761227727e5082f128eeb90bbf5" +checksum = "3c943daedff228392b791b33bba32e75737756e80a613e32e246c6ce9cbab20a" dependencies = [ "linkme-impl", ] [[package]] name = "linkme-impl" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dccda732e04fa3baf2e17cf835bfe2601c7c2edafd64417c627dabae3a8cda" +checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -784,9 +812,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" @@ -799,7 +827,7 @@ name = "minion_rs" version = "0.0.1" dependencies = [ "anyhow", - "bindgen", + "bindgen 0.69.4", "glob", "libc", "thiserror", @@ -807,11 +835,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -832,9 +860,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -842,9 +870,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -857,9 +885,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -867,22 +895,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "powerfmt" @@ -892,18 +920,21 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" -version = "0.2.17" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.65", + "syn 2.0.77", ] [[package]] @@ -929,7 +960,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.5.0", + "bitflags", "lazy_static", "num-traits", "rand", @@ -948,7 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -959,8 +990,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -977,9 +1008,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1034,18 +1065,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1055,9 +1086,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1066,15 +1097,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1084,22 +1115,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -1115,9 +1146,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1147,9 +1178,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "serde_derive_internals", - "syn 2.0.65", + "syn 2.0.77", ] [[package]] @@ -1160,33 +1191,33 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1200,26 +1231,36 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.119" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "serde_with" -version = "3.8.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "079f3a42cd87588d924ed95b533f8d30a483388c4e400ab736a7058e34f16169" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.6", + "indexmap 2.5.0", "serde", "serde_derive", "serde_json", @@ -1229,14 +1270,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc03aad67c1d26b7de277d51c86892e7d9a0110a2fe44bf6b26cc569fba302d6" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1263,15 +1304,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "structured-logger" @@ -1300,22 +1335,22 @@ checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "rustversion", - "syn 2.0.65", + "syn 2.0.77", ] [[package]] name = "sval" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a2b3c2dc4e86741e7631ad50ff798c0df4a37b81366f2072f8948805e706e9" +checksum = "eaf38d1fa2ce984086ea42fb856a9f374d94680a4f796831a7fc868d7f2af1b9" [[package]] name = "sval_buffer" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "787c386d99b431292aab95054c555899b259cf069a358dee1b252a76ca8785e3" +checksum = "81682ff859964ca1d7cf3d3d0f9ec7204ea04c2c32acb8cc2cf68ecbd3127354" dependencies = [ "sval", "sval_ref", @@ -1323,18 +1358,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584324174c3c6191a15e0f732b1b34352a87da1a82645644854494f018279ca1" +checksum = "2a213b93bb4c6f4c9f9b17f2e740e077fd18746bbf7c80c72bbadcac68fa7ee4" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab125b30b0d250c8821d39e697f073a0e52bca1159c8a14333fdc1e82ab6ef" +checksum = "6902c6d3fb52c89206fe0dc93546c0123f7d48b5997fd14e61c9e64ff0b63275" dependencies = [ "itoa", "ryu", @@ -1343,9 +1378,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3937398726f8368bcd42d5a18a9b885668e3f183e07f2b845d5a6a52613c0de2" +checksum = "11a28041ea78cdc394b930ae6b897d36246dc240a29a6edf82d76562487fb0b4" dependencies = [ "itoa", "ryu", @@ -1354,9 +1389,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c1d35fcbcf0927417de2d558e2b17287eebfd0f1d1e4cd58728b76402428f8" +checksum = "850346e4b0742a7f2fd2697d703ff80084d0b658f0f2e336d71b8a06abf9b68e" dependencies = [ "sval", "sval_buffer", @@ -1365,18 +1400,18 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82694f1a7b53e7765dfe1e9ff1719a05148b647bbac334f8de648b2cd4cd74c5" +checksum = "824afd97a8919f28a35b0fdea979845cc2ae461a8a3aaa129455cb89c88bb77a" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.12.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf83cd41641c035f9f341bac7c465f5a553dec3b5b06d7691bfd633ab9b05106" +checksum = "8ada7520dd719ed672c786c7db7de4f5230f4d504b0821bd8305cd30ca442315" dependencies = [ "serde", "sval", @@ -1401,18 +1436,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.65" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", - "quote 1.0.36", + "quote 1.0.37", "unicode-ident", ] @@ -1427,34 +1462,44 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1490,9 +1535,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.36.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -1500,6 +1545,60 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +dependencies = [ + "indexmap 2.5.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trybuild" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" @@ -1514,15 +1613,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-xid" @@ -1538,6 +1637,8 @@ dependencies = [ "proptest", "proptest-derive 0.5.0", "thiserror", + "trybuild", + "uniplate_derive 0.1.0", ] [[package]] @@ -1555,11 +1656,12 @@ dependencies = [ name = "uniplate_derive" version = "0.1.0" dependencies = [ + "itertools 0.12.1", + "lazy_static", "log", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", - "uniplate 0.1.0", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1569,22 +1671,22 @@ source = "git+https://github.com/conjure-cp/conjure-oxide.git?rev=0dc7090f0388d9 dependencies = [ "log", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", "uniplate 0.1.0 (git+https://github.com/conjure-cp/conjure-oxide.git?rev=0dc7090f0388d9a4fc0460d7d5fbf79da7854170)", ] [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "value-bag" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -1592,9 +1694,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc35703541cbccb5278ef7b589d79439fc808ff0b5867195a3230f9a47421d39" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" dependencies = [ "erased-serde", "serde", @@ -1603,9 +1705,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "285b43c29d0b4c0e65aad24561baee67a1b69dc9be9375d4a85138cbf556f7f8" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" dependencies = [ "sval", "sval_buffer", @@ -1618,15 +1720,15 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "versions" -version = "6.3.0" +version = "6.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc28d1172a20e32754969ea1a873c2c6e68e36c449c6056aa3e2ee5fe69a794" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" dependencies = [ "itertools 0.13.0", "nom", @@ -1659,57 +1761,58 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", - "quote 1.0.36", - "syn 2.0.65", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "which" @@ -1723,44 +1826,22 @@ dependencies = [ "rustix", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -1769,119 +1850,108 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" +name = "windows_x86_64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "windows_x86_64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" +name = "winnow" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote 1.0.37", + "syn 2.0.77", +] diff --git a/crates/uniplate/.gitignore b/crates/uniplate/.gitignore new file mode 100644 index 0000000000..1d7af4d36c --- /dev/null +++ b/crates/uniplate/.gitignore @@ -0,0 +1 @@ +wip/ diff --git a/crates/uniplate/Cargo.toml b/crates/uniplate/Cargo.toml index 0776800a73..b2788c3bd0 100644 --- a/crates/uniplate/Cargo.toml +++ b/crates/uniplate/Cargo.toml @@ -12,9 +12,17 @@ im = {version = "15.1.0", features = ["proptest"]} proptest = "1.5.0" proptest-derive = "0.5.0" thiserror = "1.0.61" +uniplate_derive = {path="../uniplate_derive"} + +[dev-dependencies] +trybuild = "1.0.91" [lints] workspace = true [features] unstable = [] + +[profile.test.package.proptest] +opt-level = 3 + diff --git a/crates/uniplate/examples/stmt.rs b/crates/uniplate/examples/stmt.rs new file mode 100644 index 0000000000..9b25b7df5e --- /dev/null +++ b/crates/uniplate/examples/stmt.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] + +use uniplate::{biplate::Biplate, Uniplate}; + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate()] +#[biplate(to=String,walk_into=[Expr])] +enum Stmt { + Nothing, + Assign(String, Expr), + Sequence(Vec), + If(Expr, Box, Box), + While(Expr, Box), +} + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate()] +#[biplate(to=String)] +enum Expr { + Add(Box, Box), + Sub(Box, Box), + Mul(Box, Box), + Div(Box, Box), + Val(i32), + Var(String), + Neg(Box), +} + +pub fn main() { + use Expr::*; + use Stmt::*; + + let stmt_1 = Assign("x".into(), Div(Box::new(Val(2)), Box::new(Var("y".into())))); + + let strings_in_stmt_1 = >::universe_bi(&stmt_1); + + // Test multi-type traversals + assert_eq!(strings_in_stmt_1.len(), 2); + assert!(strings_in_stmt_1.contains(&"x".into())); + assert!(strings_in_stmt_1.contains(&"y".into())); +} diff --git a/crates/uniplate/proptest-regressions/test_common/paper.txt b/crates/uniplate/proptest-regressions/test_common/paper.txt new file mode 100644 index 0000000000..916e4543ce --- /dev/null +++ b/crates/uniplate/proptest-regressions/test_common/paper.txt @@ -0,0 +1 @@ +cc 733ff88a8edcbb5d1b8a44f18ebff7d61962df8aced5c82ce45c603045bb8d30 # shrinks to ast = If(Val(0), Assign("", Val(0)), Assign("", Val(0))), new_children = [Assign("", Val(0)), Assign("", Val(1))] diff --git a/crates/uniplate/src/biplate.rs b/crates/uniplate/src/biplate.rs index e8804d9678..15664c4df8 100644 --- a/crates/uniplate/src/biplate.rs +++ b/crates/uniplate/src/biplate.rs @@ -66,7 +66,7 @@ where ctx(children.map(op)) } - /// Gest all children of a node, including itself and all children. + /// Gets all children of a node, including itself and all children. fn universe(&self) -> im::Vector { let mut results = vector![self.clone()]; for child in self.children() { diff --git a/crates/uniplate/src/impls.rs b/crates/uniplate/src/impls.rs new file mode 100644 index 0000000000..ae7222eeb1 --- /dev/null +++ b/crates/uniplate/src/impls.rs @@ -0,0 +1,39 @@ +//! Implementations of Uniplate and Biplate for common types +//! +//! This includes stdlib types as well as common collections +//! +//! Box types are excluded, and are inlined by the macro. + +// NOTE (niklasdewally): my assumption is that we can do all this here, and that llvm will inline +// this and/or devirtualise the Box when necessary to make this fast. +// https://users.rust-lang.org/t/why-box-dyn-fn-is-the-same-fast-as-normal-fn/96392 + +use im::Vector; +use std::collections::VecDeque; + +use crate::biplate::*; +use crate::derive_iter; +use crate::derive_unplateable; +use crate::Tree::*; + +derive_unplateable!(i8); +derive_unplateable!(i16); +derive_unplateable!(i32); +derive_unplateable!(i64); +derive_unplateable!(i128); +derive_unplateable!(u8); +derive_unplateable!(u16); +derive_unplateable!(u32); +derive_unplateable!(u64); +derive_unplateable!(u128); +derive_unplateable!(String); + +/*****************************/ +/* Collections */ +/*****************************/ + +// Implement Biplate for collections by converting them to iterators. + +derive_iter!(Vec); +derive_iter!(VecDeque); +derive_iter!(Vector); diff --git a/crates/uniplate/src/lib.rs b/crates/uniplate/src/lib.rs index c448b7f7cd..2a74c4fe98 100644 --- a/crates/uniplate/src/lib.rs +++ b/crates/uniplate/src/lib.rs @@ -1,101 +1,208 @@ +//! Uniplate provides simple and low-boilerplate ways to traverse and manipulate data structures. //! A port of Haskell's [Uniplate](https://hackage.haskell.org/package/uniplate) in Rust. //! //! -//! # Examples +//! # Getting Started //! -//! ## A Calculator Input Language +//! *Adapted from (Mitchell and Runciman 2009)* //! -//! Consider the AST of a calculator input language: +//! Consider the abstract syntax tree for a simple calculator language: //! +//! ```rust +//! enum Expr { +//! Add(Box, Box), +//! Sub(Box, Box), +//! Mul(Box, Box), +//! Div(Box, Box), +//! Val(i32), +//! Var(String), +//! Neg(Box), +//! } +//! ``` +//! +//! Say we want to list all the used variable names inside a given expression: +//! +//! ```rust +//! # use uniplate::test_common::paper::Expr::*; +//! # use uniplate::test_common::paper::Expr; +//! fn vars(expr: &Expr) -> Vec{ +//! match expr { +//! Add(a,b) => { +//! [vars(a),vars(b)].concat() +//! }, +//! Sub(a,b) => { +//! [vars(a),vars(b)].concat() +//! }, +//! Mul(a,b) => { +//! [vars(a),vars(b)].concat() +//! }, +//! Div(a,b) => { +//! [vars(a),vars(b)].concat() +//! }, +//! Val(a) => { +//! Vec::new() +//! }, +//! Var(a) => { +//! vec![a.clone()] +//! }, +//! Neg(a) =>{ +//! vars(a) +//! } +//! } +//! } //! ``` -//! pub enum AST { -//! Int(i32), -//! Add(Box,Box), -//! Sub(Box,Box), -//! Div(Box,Box), -//! Mul(Box,Box) +//! +//! Functions like these are annoying to write: the first 4 constructors are basically identical, +//! adding a new expression type requires a new line to be added to all match statement, and this +//! code cannot be shared with similar functions (e.g. one that change all the variable names). +//! +//! +//! With Uniplate, this boilerplate can be eliminated: +//! +//! ```rust +//! # use uniplate::test_common::paper::Expr::*; +//! # use uniplate::test_common::paper::Expr; +//! use uniplate::biplate::Biplate; +//! fn vars(expr: &Expr) -> Vec{ +//! >::universe_bi(expr).into_iter().collect() //! } -//!``` +//! ``` +//! +//! The functionality of Uniplate comes from two main traits: [`Uniplate`](biplate::Uniplate) and +//! [`Biplate`](biplate::Biplate). //! -//! Using uniplate, one can implement a single function for this AST that can be used in a whole -//! range of traversals. +//! * The [`Uniplate`](biplate::Uniplate) of `Expr` operates over all nested `Expr`s. +//! * The [`Biplate`](biplate::Biplate) of `Expr` operates over all nested values of some given type `T` within the +//! expression tree. //! -//! While this does not seem helpful in this toy example, the benefits amplify when the number of -//! enum variants increase, and the different types contained in their fields increase. +//! These traits provide traversal operations (e.g. [`children`](Uniplate::children)) as well as +//! functional programming constructs such as [`map`](Uniplate::map) and [`fold`](Uniplate::fold). +//! See the trait documentation for the full list of operations provided. //! +//! The easiest way to use Uniplate is with the derive macro. //! -//! The below example implements [`Uniplate`](uniplate::Uniplate) for this language AST, and uses uniplate methods to -//! evaluate the encoded equation. +//! ## Derive Macro //! -//!``` -//! use uniplate::uniplate::{Uniplate, UniplateError}; +//! When no arguments are provided, the macro derives a Uniplate instance: //! -//! #[derive(Clone,Eq,PartialEq,Debug)] -//! pub enum AST { -//! Int(i32), -//! Add(Box,Box), -//! Sub(Box,Box), -//! Div(Box,Box), -//! Mul(Box,Box) +//! ```rust +//! use uniplate::Uniplate; +//! #[derive(Clone,PartialEq,Eq,Debug,Uniplate)] +//! enum Expr { +//! Add(Box, Box), +//! Sub(Box, Box), +//! Mul(Box, Box), +//! Div(Box, Box), +//! Val(i32), +//! Var(String), +//! Neg(Box), //! } +//! ``` //! -//! // In the future would be automatically derived. -//! impl Uniplate for AST { -//! fn uniplate(&self) -> (Vec, Box) -> Result +'_>) { -//! let context: Box) -> Result> = match self { -//! AST::Int(i) => Box::new(|_| Ok(AST::Int(*i))), -//! AST::Add(_, _) => Box::new(|exprs: Vec| Ok(AST::Add(Box::new(exprs[0].clone()),Box::new(exprs[1].clone())))), -//! AST::Sub(_, _) => Box::new(|exprs: Vec| Ok(AST::Sub(Box::new(exprs[0].clone()),Box::new(exprs[1].clone())))), -//! AST::Div(_, _) => Box::new(|exprs: Vec| Ok(AST::Div(Box::new(exprs[0].clone()),Box::new(exprs[1].clone())))), -//! AST::Mul(_, _) => Box::new(|exprs: Vec| Ok(AST::Mul(Box::new(exprs[0].clone()),Box::new(exprs[1].clone())))) -//! }; -//! -//! let children: Vec = match self { -//! AST::Add(a,b) => vec![*a.clone(),*b.clone()], -//! AST::Sub(a,b) => vec![*a.clone(),*b.clone()], -//! AST::Div(a,b) => vec![*a.clone(),*b.clone()], -//! AST::Mul(a,b) => vec![*a.clone(),*b.clone()], -//! _ => vec![] -//! }; -//! -//! (children,context) -//! } +//! To derive Biplate instances, use the `#[biplate]` attribute: +//! +//! ```rust +//! use uniplate::Uniplate; +//! #[derive(Clone,PartialEq,Eq,Debug,Uniplate)] +//! #[biplate(to=String)] +//! #[biplate(to=i32)] +//! enum Expr { +//! Add(Box, Box), +//! Sub(Box, Box), +//! Mul(Box, Box), +//! Div(Box, Box), +//! Val(i32), +//! Var(String), +//! Neg(Box), //! } +//! ``` //! -//! pub fn my_rule(e: AST) -> AST{ -//! match e { -//! AST::Int(a) => AST::Int(a), -//! AST::Add(a,b) => {match (&*a,&*b) { (AST::Int(a), AST::Int(b)) => AST::Int(a+b), _ => AST::Add(a,b) }} -//! AST::Sub(a,b) => {match (&*a,&*b) { (AST::Int(a), AST::Int(b)) => AST::Int(a-b), _ => AST::Sub(a,b) }} -//! AST::Mul(a,b) => {match (&*a,&*b) { (AST::Int(a), AST::Int(b)) => AST::Int(a*b), _ => AST::Mul(a,b) }} -//! AST::Div(a,b) => {match (&*a,&*b) { (AST::Int(a), AST::Int(b)) => AST::Int(a/b), _ => AST::Div(a,b) }} -//! } +//! ## Multi-type traversals +//! +//! Lets expand our calculator language to include statements as well as expressions: +//! +//! ```rust +//! enum Expr { +//! Add(Box, Box), +//! Sub(Box, Box), +//! Mul(Box, Box), +//! Div(Box, Box), +//! Val(i32), +//! Var(String), +//! Neg(Box), //! } -//! pub fn main() { -//! let ast = AST::Add( -//! Box::new(AST::Int(1)), -//! Box::new(AST::Mul( -//! Box::new(AST::Int(2)), -//! Box::new(AST::Div( -//! Box::new(AST::Add(Box::new(AST::Int(1)),Box::new(AST::Int(2)))), -//! Box::new(AST::Int(3)) -//! ))))); -//! -//! let new_ast = ast.transform(my_rule); -//! assert!(new_ast.is_ok()); -//! println!("{:?}",new_ast); -//! assert_eq!(new_ast.unwrap(), AST::Int(3)); +//! +//! enum Stmt { +//! Assign(String, Expr), +//! Sequence(Vec), +//! If(Expr, Box, Box), +//! While(Expr, Box), //! } //! ``` //! -//! ....MORE DOCS TO COME.... +//! When looking for `Strings` in a given `Stmt`, we may want to identify not only the strings +//! directly contained within a `Stmt`, but also any strings contained inside an `Expr` inside a +//! `Stmt`. +//! +//! For example: +//! +//! ``` +//! # use uniplate::test_common::paper::Expr::*; +//! # use uniplate::test_common::paper::Expr::*; +//! # use uniplate::test_common::paper::Stmt; +//! # use uniplate::test_common::paper::Stmt::*; +//! use uniplate::biplate::Biplate; +//! +//! let stmt = Assign("x".into(), Add(Box::new(Var("y".into())),Box::new(Val(10)))); +//! let strings = >::universe_bi(&stmt); //! -//! # Acknowledgements / Related Work +//! assert!(strings.contains(&"x".into())); //! -//! *This crate implements programming constructs from the following Haskell libraries and -//! papers:* +//! // Despite being inside an Expr::String, "y" is found by Biplate +//! assert!(strings.contains(&"y".into())); +//! +//! assert_eq!(strings.len(), 2); +//! ``` +//! +//! +//! To do this, a list of types to "walk into" can be given as an argument to the Biplate +//! declaration: +//! ```rust +//! use uniplate::Uniplate; +//! #[derive(Clone,PartialEq,Eq,Debug,Uniplate)] +//! #[uniplate()] +//! #[biplate(to=String,walk_into=[Expr])] +//! #[biplate(to=Stmt)] +//! enum Expr { +//! Add(Box, Box), +//! Sub(Box, Box), +//! Mul(Box, Box), +//! Div(Box, Box), +//! Val(i32), +//! Var(String), +//! Neg(Box), +//! } +//! +//! // Uniplate also supports walk_into. +//! // In this case, it doesn't do much. +//! #[derive(Clone,PartialEq,Eq,Debug,Uniplate)] +//! #[biplate(to=String, walk_into=[Expr])] +//! #[biplate(to=Expr,walk_into=[Expr])] +//! #[uniplate(walk_into=[Expr])] +//! enum Stmt { +//! Assign(String, Expr), +//! Sequence(Vec), +//! If(Expr, Box, Box), +//! While(Expr, Box), +//! } +//! ``` +//! +//! +//! # Bibliography +//! +//! The techniques implemented in this crate originate from the following: //! -//! * [Uniplate](https://hackage.haskell.org/package/uniplate). +//! * [The Uniplate Haskell Library](https://hackage.haskell.org/package/uniplate). //! //! * Neil Mitchell and Colin Runciman. 2007. Uniform boilerplate and list processing. In //! Proceedings of the ACM SIGPLAN workshop on Haskell workshop (Haskell '07). Association for @@ -106,10 +213,143 @@ //! [(free copy)](https://www.cambridge.org/core/services/aop-cambridge-core/content/view/0C058890B8A9B588F26E6D68CF0CE204/S0956796897002864a.pdf/zipper.pdf) pub mod biplate; +pub mod impls; mod tree; -pub mod uniplate; +//pub mod uniplate; pub use tree::Tree; #[doc(hidden)] pub mod test_common; + +pub use uniplate_derive::*; + +extern crate self as uniplate; + +/// Generates a Biplate and Uniplate instance for an unplateable type. +#[macro_export] +macro_rules! derive_unplateable { + ($t:ty) => { + impl Uniplate for $t { + fn uniplate(&self) -> (Tree, Box) -> Self>) { + let val = self.clone(); + (::uniplate::Tree::Zero, Box::new(move |_| val.clone())) + } + } + + impl Biplate<$t> for $t { + fn biplate(&self) -> (Tree<$t>, Box) -> $t>) { + let val = self.clone(); + ( + ::uniplate::Tree::One(val.clone()), + Box::new(move |_| val.clone()), + ) + } + } + }; +} + +// Generates a Biplate and Uniplate instance for an iterable type. +#[macro_export] +macro_rules! derive_iter { + ($iter_ty:ident) => { + impl Biplate for $iter_ty + where + T: Clone + Eq + Uniplate + Sized + 'static, + F: Clone + Eq + Uniplate + Biplate + Sized + 'static, + { + fn biplate(&self) -> (Tree, Box<(dyn Fn(Tree) -> $iter_ty)>) { + if (self.is_empty()) { + let val = self.clone(); + return (Tree::Zero, Box::new(move |_| val.clone())); + } + + // this is an example of the special biplate case discussed in the paper. + // T == F: return all types F in the Vector. + if std::any::TypeId::of::() == std::any::TypeId::of::() { + unsafe { + // need to cast from F to T + let children: Tree = Tree::Many( + self.clone() + .into_iter() + .map(|x: F| { + // possibly unsafe, definitely stupid, but seems to be the only way here? + let x: T = std::mem::transmute::<&F, &T>(&x).clone(); + Tree::One(x) + }) + .collect(), + ); + + let ctx: Box) -> $iter_ty> = + Box::new(move |new_tree: Tree| { + let Tree::Many(xs) = new_tree else { + todo!(); + }; + xs.into_iter() + .map(|x| { + let Tree::One(x) = x else { + todo!(); + }; + let x: F = std::mem::transmute::<&T, &F>(&x).clone(); + x + }) + .collect() + }); + + return (children, ctx); + } + } + + // T != F: return all type T's contained in the type F's in the vector + let mut child_trees: im::Vector> = im::Vector::new(); + let mut child_ctxs: Vec) -> F>> = Vec::new(); + for item in self { + let (tree, plate) = >::biplate(item); + child_trees.push_back(tree); + child_ctxs.push(plate); + } + + let tree = Tree::Many(child_trees); + let ctx = Box::new(move |new_tree: Tree| { + let mut out = Vec::::new(); + let Tree::Many(new_trees) = new_tree else { + todo!() + }; + for (child_tree, child_ctx) in std::iter::zip(new_trees, &child_ctxs) { + out.push(child_ctx(child_tree)); + } + out.into_iter().collect::<$iter_ty>() + }); + (tree, ctx) + } + } + + // Traversal Biplate + impl Uniplate for $iter_ty + where + T: Clone + Eq + Uniplate + Sized + 'static, + { + fn uniplate(&self) -> (Tree, Box) -> Self>) { + let val = self.clone(); + (Zero, Box::new(move |_| val.clone())) + } + } + }; +} + +#[macro_export] +macro_rules! unreachable { + ($from:ident,$to:ident) => { + impl ::uniplate::biplate::Biplate<$to> for $from { + fn biplate( + &self, + ) -> ( + ::uniplate::Tree<$to>, + Box) -> $from>, + ) { + let val = self.clone(); + (::uniplate::Tree::Zero, Box::new(move |_| val.clone())) + } + } + }; +} diff --git a/crates/uniplate/src/test_common/paper.rs b/crates/uniplate/src/test_common/paper.rs index 57ceef0456..357a181c44 100644 --- a/crates/uniplate/src/test_common/paper.rs +++ b/crates/uniplate/src/test_common/paper.rs @@ -1,9 +1,15 @@ +use crate::biplate::Uniplate; +use crate::Uniplate; +use prop::sample::SizeRange; use proptest::prelude::*; // Examples found in the Uniplate paper. // Stmt and Expr to demonstrate and test multitype traversals. -#[derive(Eq, PartialEq, Clone, Debug)] +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[biplate(to=Expr)] +#[biplate(to=String,walk_into=[Expr])] +#[uniplate(walk_into=[Expr])] pub enum Stmt { Assign(String, Expr), Sequence(Vec), @@ -11,7 +17,10 @@ pub enum Stmt { While(Expr, Box), } -#[derive(Eq, PartialEq, Clone, Debug)] +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate(walk_into=[Stmt])] +#[biplate(to=String)] +#[biplate(to=Stmt)] pub enum Expr { Add(Box, Box), Sub(Box, Box), @@ -25,17 +34,17 @@ pub enum Expr { use self::Expr::*; use self::Stmt::*; pub fn proptest_exprs() -> impl Strategy { - let leafs = prop_oneof![any::().prop_map(Val), any::().prop_map(Var),]; + let leafs = prop_oneof![any::().prop_map(Val), "[a-z]*".prop_map(Var),]; - leafs.prop_recursive(10, 512, 2, |inner| { + leafs.prop_recursive(8, 256, 10, |inner| { prop_oneof![ - prop::collection::vec(inner.clone(), 2..2) + prop::collection::vec(inner.clone(), 2..3) .prop_map(|elems| Add(Box::new(elems[0].clone()), Box::new(elems[1].clone()))), - prop::collection::vec(inner.clone(), 2..2) + prop::collection::vec(inner.clone(), 2..3) .prop_map(|elems| Sub(Box::new(elems[0].clone()), Box::new(elems[1].clone()))), - prop::collection::vec(inner.clone(), 2..2) + prop::collection::vec(inner.clone(), 2..3) .prop_map(|elems| Mul(Box::new(elems[0].clone()), Box::new(elems[1].clone()))), - prop::collection::vec(inner.clone(), 2..2) + prop::collection::vec(inner.clone(), 2..3) .prop_map(|elems| Div(Box::new(elems[0].clone()), Box::new(elems[1].clone()))), inner.prop_map(|inner| Neg(Box::new(inner.clone()))) ] @@ -43,11 +52,11 @@ pub fn proptest_exprs() -> impl Strategy { } pub fn proptest_stmts() -> impl Strategy { - let leafs = prop_oneof![(".*", proptest_exprs()).prop_map(|(a, b)| Assign(a, b)),]; + let leafs = prop_oneof![("[a-z]*", proptest_exprs()).prop_map(|(a, b)| Assign(a, b)),]; - leafs.prop_recursive(10, 512, 50, |inner| { + leafs.prop_recursive(8, 256, 10, |inner| { prop_oneof![ - (proptest_exprs(), prop::collection::vec(inner.clone(), 2..2)).prop_map( + (proptest_exprs(), prop::collection::vec(inner.clone(), 2..4)).prop_map( move |(expr, stmts)| If( expr, Box::new(stmts[0].clone()), @@ -56,7 +65,27 @@ pub fn proptest_stmts() -> impl Strategy { ), (proptest_exprs(), inner.clone()) .prop_map(move |(expr, stmt)| While(expr, Box::new(stmt))), - prop::collection::vec(inner.clone(), 0..50).prop_map(Sequence) + prop::collection::vec(inner.clone(), 0..10).prop_map(Sequence) ] }) } + +proptest! { + +#![proptest_config(ProptestConfig { max_shrink_iters:1000000, max_global_rejects:100000,cases:50,..Default::default()})] + +#[test] +fn uniplate_children(ast in proptest_stmts(), new_children in proptest::collection::vec(proptest_stmts(),1..=10)) { + let original_children = ast.children(); + prop_assume!(original_children.len() == new_children.len()); + + let mut ast = ast.with_children(new_children.clone().into()); + + prop_assert_eq!(im::Vector::::from(new_children),ast.children()); + + ast = ast.with_children(original_children.clone()); + prop_assert_eq!(original_children,ast.children()); + +} + +} diff --git a/crates/uniplate/tests/derive-fail/no-uniplate-attr.rs.disabled b/crates/uniplate/tests/derive-fail/no-uniplate-attr.rs.disabled new file mode 100644 index 0000000000..d49c49d8f4 --- /dev/null +++ b/crates/uniplate/tests/derive-fail/no-uniplate-attr.rs.disabled @@ -0,0 +1,17 @@ +use uniplate::Uniplate; +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +enum Stmt { + Assign(String, Expr), + //Sequence(Vec), + If(Expr, Box, Box), + While(Expr, Box), +} + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate()] +enum Expr {} + +pub fn main() { + +} + diff --git a/crates/uniplate/tests/derive-fail/no-uniplate-attr.stderr b/crates/uniplate/tests/derive-fail/no-uniplate-attr.stderr new file mode 100644 index 0000000000..9287dad6e4 --- /dev/null +++ b/crates/uniplate/tests/derive-fail/no-uniplate-attr.stderr @@ -0,0 +1,5 @@ +error: expected a uniplate declaration e.g. #[uniplate(walk_into=(Expr,Stmt))] + --> tests/derive-fail/no-uniplate-attr.rs:3:1 + | +3 | enum Stmt { + | ^^^^ diff --git a/crates/uniplate/tests/derive-pass/allow-enum-fields-with-trailing-commas.rs b/crates/uniplate/tests/derive-pass/allow-enum-fields-with-trailing-commas.rs new file mode 100644 index 0000000000..5e8049371f --- /dev/null +++ b/crates/uniplate/tests/derive-pass/allow-enum-fields-with-trailing-commas.rs @@ -0,0 +1,17 @@ +use uniplate::Uniplate; + +#[derive(Uniplate,PartialEq,Eq,Clone)] +#[uniplate()] +enum NoTrailingComma { + B(Vec) +} + +#[derive(Uniplate,PartialEq,Eq,Clone)] +#[uniplate()] +enum TrailingCommaInField { + B(Vec,) +} + +pub fn main() { + +} diff --git a/crates/uniplate/tests/derive-pass/allow-enum-variant-with-no-fields.rs b/crates/uniplate/tests/derive-pass/allow-enum-variant-with-no-fields.rs new file mode 100644 index 0000000000..2c97f25c23 --- /dev/null +++ b/crates/uniplate/tests/derive-pass/allow-enum-variant-with-no-fields.rs @@ -0,0 +1,8 @@ +use uniplate::Uniplate; +#[derive(Clone, PartialEq, Eq, Uniplate)] +#[uniplate()] +enum A { + B, + C, +} +pub fn main() {} diff --git a/crates/uniplate/tests/derive-pass/allow-enums-with-trailing-commas.rs b/crates/uniplate/tests/derive-pass/allow-enums-with-trailing-commas.rs new file mode 100644 index 0000000000..e06eda3a1b --- /dev/null +++ b/crates/uniplate/tests/derive-pass/allow-enums-with-trailing-commas.rs @@ -0,0 +1,18 @@ +use uniplate::Uniplate; + +#[derive(PartialEq,Eq,Clone,Uniplate)] +#[uniplate()] +enum NoTrailingComma { + B(Vec) +} + +#[derive(PartialEq,Eq,Clone,Uniplate)] +#[uniplate()] +enum TrailingComma { + B(Vec), +} + +pub fn main() { + +} + diff --git a/crates/uniplate/tests/derive-pass/allow-nested-vec2.rs.ignore b/crates/uniplate/tests/derive-pass/allow-nested-vec2.rs.ignore new file mode 100644 index 0000000000..9873eb7d4c --- /dev/null +++ b/crates/uniplate/tests/derive-pass/allow-nested-vec2.rs.ignore @@ -0,0 +1,9 @@ +use uniplate::Uniplate; +#[derive(PartialEq,Eq,Clone,Uniplate)] +#[uniplate()] +enum MyEnum { + A(Vec>), + B(Vec>>) +} + +pub fn main() {} diff --git a/crates/uniplate/tests/derive-pass/biplate-stmt.rs b/crates/uniplate/tests/derive-pass/biplate-stmt.rs new file mode 100644 index 0000000000..edd7999ca0 --- /dev/null +++ b/crates/uniplate/tests/derive-pass/biplate-stmt.rs @@ -0,0 +1,43 @@ +#![allow(dead_code)] + +use uniplate::{biplate::Biplate, Uniplate}; + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[biplate(to=Expr)] +#[biplate(to=String,walk_into=[Expr])] +#[uniplate(walk_into=[Expr])] +enum Stmt { + Assign(String, Expr), + Sequence(Vec), + If(Expr, Box, Box), + While(Expr, Box), +} + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate(walk_into=[Stmt])] +#[biplate(to=String)] +#[biplate(to=Stmt)] +enum Expr { + Nothing, + Add(Box, Box), + Sub(Box, Box), + Mul(Box, Box), + Div(Box, Box), + Val(i32), + Var(String), + Neg(Box), +} + +pub fn main() { + use Expr::*; + use Stmt::*; + + let stmt_1 = Assign("x".into(), Div(Box::new(Val(2)), Box::new(Var("y".into())))); + + let strings_in_stmt_1 = >::universe_bi(&stmt_1); + + // Test multi-type traversals + assert_eq!(strings_in_stmt_1.len(), 2); + assert!(strings_in_stmt_1.contains(&"x".into())); + assert!(strings_in_stmt_1.contains(&"y".into())); +} diff --git a/crates/uniplate/tests/derive-pass/conjure_ast.rs b/crates/uniplate/tests/derive-pass/conjure_ast.rs new file mode 100644 index 0000000000..85ddb3f8ef --- /dev/null +++ b/crates/uniplate/tests/derive-pass/conjure_ast.rs @@ -0,0 +1,109 @@ +// Does Uniplating Conjure-Oxide Expressions Compile? +// +// Using the AST as of 30/04/2024 +// -- Niklas Dewally + +use core::fmt::Display; +use core::fmt::Formatter; +use uniplate::Uniplate; +#[derive(Clone, Debug, PartialEq, Eq, Uniplate)] +#[uniplate()] +#[biplate(to=Constant)] +#[biplate(to=String,walk_into=[Name])] +enum Expression { + Nothing, + Bubble(Metadata, Box, Box), + Constant(Metadata, Constant), + Reference(Metadata, Name), + Sum(Metadata, Vec), + Min(Metadata, Vec), + Not(Metadata, Box), + Or(Metadata, Vec), + And(Metadata, Vec), + Eq(Metadata, Box, Box), + Neq(Metadata, Box, Box), + Geq(Metadata, Box, Box), + Leq(Metadata, Box, Box), + Gt(Metadata, Box, Box), + Lt(Metadata, Box, Box), + SafeDiv(Metadata, Box, Box), + UnsafeDiv(Metadata, Box, Box), + SumEq(Metadata, Vec, Box), + SumGeq(Metadata, Vec, Box), + SumLeq(Metadata, Vec, Box), + DivEq(Metadata, Box, Box, Box), + Ineq(Metadata, Box, Box, Box), + AllDiff(Metadata, Vec), +} + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash,Uniplate)] +#[uniplate()] +#[biplate(to=String)] +enum Name { + UserName(String), + MachineName(i32), +} + +impl Display for Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Name::UserName(s) => write!(f, "UserName({})", s), + Name::MachineName(i) => write!(f, "MachineName({})", i), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct Metadata { + pub clean: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq,Uniplate)] +#[uniplate()] +pub enum Constant { + Int(i32), + Bool(bool), +} + +impl TryFrom for i32 { + type Error = &'static str; + + fn try_from(value: Constant) -> Result { + match value { + Constant::Int(i) => Ok(i), + _ => Err("Cannot convert non-i32 Constant to i32"), + } + } +} +impl TryFrom for bool { + type Error = &'static str; + + fn try_from(value: Constant) -> Result { + match value { + Constant::Bool(b) => Ok(b), + _ => Err("Cannot convert non-bool Constant to bool"), + } + } +} + +impl From for Constant { + fn from(i: i32) -> Self { + Constant::Int(i) + } +} + +impl From for Constant { + fn from(b: bool) -> Self { + Constant::Bool(b) + } +} + +impl Display for Constant { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self { + Constant::Int(i) => write!(f, "Int({})", i), + Constant::Bool(b) => write!(f, "Bool({})", b), + } + } +} +pub fn main() {} diff --git a/crates/uniplate/tests/derive_test_runner.rs b/crates/uniplate/tests/derive_test_runner.rs new file mode 100644 index 0000000000..43842121e4 --- /dev/null +++ b/crates/uniplate/tests/derive_test_runner.rs @@ -0,0 +1,7 @@ +#[test] +fn derive_tests() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/derive-fail/*.rs"); + t.pass("tests/derive-pass/*.rs"); + t.pass("examples/*.rs"); +} diff --git a/crates/uniplate/tests/stmt.rs b/crates/uniplate/tests/stmt.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/uniplate/tests/stmt.rs @@ -0,0 +1 @@ + diff --git a/crates/uniplate_derive/Cargo.toml b/crates/uniplate_derive/Cargo.toml index e173f51dbb..7454c4b61f 100644 --- a/crates/uniplate_derive/Cargo.toml +++ b/crates/uniplate_derive/Cargo.toml @@ -10,10 +10,15 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.86" -syn = "2.0.65" +syn = { version = "2.0.65", features = ["derive","parsing","printing","clone-impls","proc-macro","extra-traits"] } quote = "1.0.36" log = "0.4.22" -uniplate = {path = "../uniplate"} +itertools = "0.12.1" +lazy_static = "1.4.0" -[lints] -workspace = true +[lints.rust] +unstable_name_collisions = "allow" + +[lints.clippy] +unwrap_used = "allow" +expect_used = "allow" diff --git a/crates/uniplate_derive/src/ast/data.rs b/crates/uniplate_derive/src/ast/data.rs new file mode 100644 index 0000000000..15fe561c60 --- /dev/null +++ b/crates/uniplate_derive/src/ast/data.rs @@ -0,0 +1,148 @@ +use syn::token; + +use crate::prelude::*; + +#[derive(Clone, Debug)] +pub enum Data { + DataEnum(DataEnum), +} + +impl Data { + #[allow(dead_code)] + pub fn span(&self) -> Span { + match self { + Data::DataEnum(x) => x.span, + } + } + + pub fn ident(&self) -> syn::Ident { + match self { + Data::DataEnum(x) => x.ident.clone(), + } + } +} + +impl From for ast::PlateableType { + fn from(val: Data) -> Self { + match val { + Data::DataEnum(x) => { + let mut typ_segments: Punctuated = + Punctuated::new(); + typ_segments.push(syn::PathSegment { + ident: x.ident, + arguments: syn::PathArguments::None, + }); + + let base_typ: syn::Path = syn::Path { + leading_colon: None, + segments: typ_segments, + }; + + ast::PlateableType { + base_typ, + wrapper_typ: None, + } + } + } + } +} + +impl Parse for Data { + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + let lookahead = input.lookahead1(); + if lookahead.peek(Token![enum]) { + input.parse().map(Data::DataEnum) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Clone, Debug)] +pub struct DataEnum { + pub ident: syn::Ident, + pub span: Span, + pub variants: Vec, +} +impl Parse for DataEnum { + // Layout of an enum as per: + // https://doc.rust-lang.org/stable/reference/items/enumerations.html + // https://docs.rs/syn/latest/syn/struct.ItemEnum.html + + fn parse(input: ParseStream) -> syn::Result { + //input.parse::()?; + input.parse::()?; + let ident = input.parse::()?; + + input.parse::()?; + + let content; + braced! {content in input}; + + let variants: Punctuated = + content.parse_terminated(Variant::parse, Token![,])?; + + Ok(DataEnum { + span: ident.span(), + ident, + variants: variants.into_iter().collect(), + }) + } +} + +#[derive(Clone, Debug)] +pub struct Variant { + pub ident: syn::Ident, + #[allow(dead_code)] + pub span: Span, + pub fields: Vec, +} +impl Parse for Variant { + fn parse(input: ParseStream) -> syn::Result { + // Layout of a variant as per: + // https://docs.rs/syn/latest/syn/struct.Variant.html + // https://doc.rust-lang.org/stable/reference/items/enumerations.html + + input.call(syn::Attribute::parse_outer)?; + let ident: syn::Ident = input.parse()?; + + if !input.peek(token::Paren) { + return Ok(Variant { + span: ident.span(), + ident, + fields: Default::default(), + }); + } + + let content; + parenthesized! {content in input}; + + let fields: Punctuated = content.call(Punctuated::parse_terminated)?; + Ok(Variant { + span: ident.span(), + ident, + fields: fields.into_iter().collect(), + }) + } +} + +#[derive(Clone, Debug)] +pub struct Field { + #[allow(dead_code)] + pub span: Span, + pub typ: ast::Type, +} + +impl Parse for Field { + fn parse(input: ParseStream) -> syn::Result { + // Layout of a field as per: + // https://docs.rs/syn/latest/syn/struct.Field.html + // https://doc.rust-lang.org/stable/reference/items/structs.html (tuple field) + input.call(syn::Attribute::parse_outer)?; + input.parse::()?; + let span = input.span(); + let typ: ast::Type = input.parse()?; + Ok(Field { span, typ }) + } +} diff --git a/crates/uniplate_derive/src/ast/derive_input.rs b/crates/uniplate_derive/src/ast/derive_input.rs new file mode 100644 index 0000000000..2a06f249bd --- /dev/null +++ b/crates/uniplate_derive/src/ast/derive_input.rs @@ -0,0 +1,173 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::borrow::Borrow; + +use syn::bracketed; + +use crate::prelude::*; + +#[derive(Clone, Debug)] +pub struct DeriveInput { + pub instance_metadata: Vec, + pub data: ast::Data, +} + +impl Parse for DeriveInput { + fn parse(input: ParseStream) -> syn::Result { + // ATTRIBUTES* + // DATA_DECLARATION + + let instance_metadata: Vec = input.call(InstanceMeta::parse_many)?; + let data: ast::Data = input.parse()?; + + Ok(DeriveInput { + instance_metadata, + data, + }) + } +} + +/// Parsed metadata associated with a Biplate / Uniplate instance. +/// +/// These settings are determined through the #[uniplate(...)] and #[biplate(...)] helper +/// attributes. +#[derive(Clone, Debug)] +pub enum InstanceMeta { + Uniplate(UniplateInstanceMeta), + Biplate(BiplateInstanceMeta), +} + +pub trait InstanceMetaKind { + fn from_attribute(attr: syn::Attribute) -> syn::Result; +} + +impl InstanceMeta { + /// Parses 0 or more InstanceMeta attributes. + pub fn parse_many(input: ParseStream<'_>) -> syn::Result> { + // syn parses attributes into vectors, so its easier if we do this aswell! + let attrs: Vec = input.call(syn::Attribute::parse_outer)?; + + let mut has_uniplate: bool = false; + + let mut instance_metadata: Vec = Vec::new(); + for attr in attrs { + let Some(attr_name) = attr.path().get_ident() else { + continue; + }; + + let meta = match attr_name.to_string().borrow() { + "uniplate" => { + if !has_uniplate { + has_uniplate = true; + } else { + return Err( + input.error("only one uniplate declaration is expected per type") + ); + }; + + Some(UniplateInstanceMeta::from_attribute(attr)?) + } + "biplate" => Some(BiplateInstanceMeta::from_attribute(attr)?), + _ => None, + }; + + if let Some(meta) = meta { + instance_metadata.push(meta); + }; + } + if !has_uniplate { + // Default implementation of uniplate without walking into anything + instance_metadata.push(InstanceMeta::Uniplate(Default::default())); + } + + Ok(instance_metadata) + } + + /// Checks if a type can be walked into or not + pub fn walk_into_type(&self, typ: &ast::Type) -> bool { + let walk_into = match &self { + InstanceMeta::Uniplate(u) => &u.walk_into, + InstanceMeta::Biplate(b) => &b.walk_into, + }; + + for typ2 in walk_into { + if typ.base_typ() == typ2.base_typ() { + return true; + } + } + false + } +} + +#[derive(Clone, Debug, Default)] +pub struct UniplateInstanceMeta { + walk_into: Vec, +} + +impl InstanceMetaKind for UniplateInstanceMeta { + fn from_attribute(attr: syn::Attribute) -> syn::Result { + let mut walk_into: Vec = Vec::new(); + attr.parse_nested_meta(|meta| { + // #[uniplate(walk_into=(A,B,C))] + if meta.path.is_ident("walk_into") { + meta.input.parse::()?; + let content; + bracketed!(content in meta.input); + + let typs: Punctuated = + content.call(Punctuated::parse_terminated)?; + walk_into.extend(typs.into_iter()); + return Ok(()); + }; + + Err(meta.error("unrecognized property")) + })?; + + Ok(InstanceMeta::Uniplate(UniplateInstanceMeta { walk_into })) + } +} + +#[derive(Clone, Debug)] +pub struct BiplateInstanceMeta { + pub to: ast::Type, + pub walk_into: Vec, +} + +impl InstanceMetaKind for BiplateInstanceMeta { + fn from_attribute(attr: syn::Attribute) -> syn::Result { + let mut walk_into: Vec = Vec::new(); + let mut to: Option = None; + attr.parse_nested_meta(|meta| { + // #[biplate(walk_into=(A,B,C))] + if meta.path.is_ident("walk_into") { + meta.input.parse::()?; + let content; + bracketed!(content in meta.input); + + let typs: Punctuated = + content.call(Punctuated::parse_terminated)?; + walk_into.extend(typs.into_iter()); + return Ok(()); + } + + // #[biplate(to=A)] + if meta.path.is_ident("to") { + if to.is_some() { + return Err(meta.error("only one to type can be given")); + } + meta.input.parse::()?; + to = Some(meta.input.parse()?); + return Ok(()); + } + + Err(meta.error("unrecognized property")) + })?; + + let Some(to) = to else { + return Err(syn::Error::new(attr.span(), "no to type given")); + }; + + Ok(InstanceMeta::Biplate(BiplateInstanceMeta { to, walk_into })) + } +} diff --git a/crates/uniplate_derive/src/ast/mod.rs b/crates/uniplate_derive/src/ast/mod.rs new file mode 100644 index 0000000000..0e1bc9a116 --- /dev/null +++ b/crates/uniplate_derive/src/ast/mod.rs @@ -0,0 +1,7 @@ +//! Custom AST nodes for implementing uniplate, and their parser implementations +mod data; +mod derive_input; +mod typ; +pub use data::*; +pub use derive_input::*; +pub use typ::*; diff --git a/crates/uniplate_derive/src/ast/typ.rs b/crates/uniplate_derive/src/ast/typ.rs new file mode 100644 index 0000000000..6ab476e017 --- /dev/null +++ b/crates/uniplate_derive/src/ast/typ.rs @@ -0,0 +1,259 @@ +use crate::prelude::*; +use itertools::Itertools; +use lazy_static::lazy_static; +use quote::TokenStreamExt; + +/// All valid field smart pointer - e.g Box, Vec, ... +#[derive(Clone, Debug)] +pub enum BoxType { + Box, +} + +impl ToTokens for BoxType { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + BoxType::Box => { + tokens.append_all(quote! {Box}); + } + } + } +} + +#[derive(Clone, Debug)] +pub enum Type { + BoxedPlateable(BoxedPlateableType), + Plateable(PlateableType), + Unplateable, +} + +impl Type { + pub fn base_typ(&self) -> Option { + match self { + Type::BoxedPlateable(x) => Some(x.base_typ()), + Type::Plateable(x) => Some(x.base_typ()), + Type::Unplateable => None, + } + } +} + +impl ToTokens for Type { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + Type::BoxedPlateable(x) => x.to_tokens(tokens), + Type::Plateable(x) => x.to_tokens(tokens), + Type::Unplateable => (), + } + } +} + +pub trait HasBaseType { + fn base_typ(&self) -> syn::Path; +} + +lazy_static! { + static ref BOX_PREFIXES: Vec<&'static str> = + vec!("::std::boxed::Box", "std::boxed::Box", "Box"); +} + +impl Parse for Type { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let typ: syn::Type = input.parse()?; + let syn::Type::Path(typ) = typ else { + return Ok(Type::Unplateable); + }; + + let mut base_path = typ.path.clone(); + let mut wrapper_path: Option = Some(typ.path); + + let mut box_type: Option = None; + let mut any_args = false; // special case: if we find no args wrapper type should be empty. + + // Is the outermost type a box? + let type_str: String = base_path + .segments + .iter() + .map(|x| x.ident.to_string()) + .intersperse("::".to_owned()) + .collect(); + + if BOX_PREFIXES.contains(&type_str.as_str()) { + box_type = Some(BoxType::Box); + let syn::PathArguments::AngleBracketed(args) = + &base_path.segments.last().expect("").arguments.clone() + else { + panic!(); + }; + + let syn::GenericArgument::Type(syn::Type::Path(typ2)) = args.args.first().expect("") + else { + return Err(syn::Error::new( + args.span(), + "Biplate: expected type argument here", + )); + }; + base_path = typ2.path.clone(); + + any_args = false; + } + + while let syn::PathArguments::AngleBracketed(args) = + &base_path.segments.last().expect("").arguments.clone() + { + any_args = true; + if args.args.len() != 1 { + return Err(syn::Error::new( + args.span(), + format!( + "Biplate: expected one generic argument here, got {}", + args.args.len() + ), + )); + } + + let syn::GenericArgument::Type(syn::Type::Path(typ2)) = args.args.first().expect("") + else { + return Err(syn::Error::new( + args.span(), + "Biplate: expected type argument here", + )); + }; + + base_path = typ2.path.clone(); + + // Have we just found a box type? + let type_str: String = base_path + .segments + .iter() + .map(|x| x.ident.to_string()) + .intersperse("::".to_owned()) + .collect(); + + let mut new_box_type: Option = None; + if BOX_PREFIXES.contains(&type_str.as_str()) { + new_box_type = Some(BoxType::Box); + } + + // Have a Box> - I don't know how to handle this + if new_box_type.is_some() && box_type.is_some() { + return Err(syn::Error::new( + args.span(), + "Biplate: nested Box<> is not supported.", + )); + } + + if new_box_type.is_some() { + box_type = new_box_type; + + wrapper_path = Some(base_path.clone()); + any_args = false; + } + } + + // ensure that we don't have parenthesised (...) type arguments. + let args = base_path.segments.last().expect("").arguments.clone(); + let syn::PathArguments::None = args else { + return Err(syn::Error::new( + args.span(), + "Biplate: expected no type arguments here.", + )); + }; + + if !any_args { + // if we have no arguments in our path, there is no wrapper path. + wrapper_path = None; + } else { + wrapper_path + .clone() + .expect("") + .segments + .last_mut() + .expect("") + .arguments = syn::PathArguments::None; + } + + let plateable_typ = PlateableType { + wrapper_typ: wrapper_path, + base_typ: base_path, + }; + + if let Some(box_type) = box_type { + Ok(Type::BoxedPlateable(BoxedPlateableType { + inner_typ: plateable_typ, + box_typ: box_type, + })) + } else { + Ok(Type::Plateable(plateable_typ)) + } + } +} + +/// A platable type inside a smart-pointer or cell. +/// +/// Unlike most `PlateableType`s, the conversions from Box to T are inlined in code generation +/// instead of using builtin implementations of Biplate. +/// +/// This is to avoid unnecessary moving of stuff between stack and heap - instead, we just +/// dereference the smart pointer and pass that into Biplate. +#[derive(Clone, Debug)] +pub struct BoxedPlateableType { + /// The underlying type of the field. + pub inner_typ: PlateableType, + + /// The wrapper type of the field. + pub box_typ: BoxType, +} + +impl ToTokens for BoxedPlateableType { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let base_typ = self.inner_typ.clone(); + match self.box_typ { + BoxType::Box => { + tokens.append_all(quote! {Box<#base_typ>}); + } + } + } +} + +impl HasBaseType for BoxedPlateableType { + fn base_typ(&self) -> syn::Path { + self.inner_typ.base_typ() + } +} + +/// A plateable type. +/// +/// This struct splits a type into wrapper and base components. +/// Base types are used to determine what new instances of Biplate to derive. +/// Wrapper types are unwrapped through builtin impls of uniplate. +/// +/// For example, Vec> has the base type MyTyp and the wrapper type Vec, + + /// The innermost type of the field. + pub base_typ: syn::Path, +} + +impl ToTokens for PlateableType { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let base_typ = self.base_typ.clone(); + + if let Some(wrapper) = self.wrapper_typ.clone() { + tokens.append_all(quote!(#wrapper)); + } else { + tokens.append_all(quote!(#base_typ)); + } + } +} + +impl HasBaseType for PlateableType { + fn base_typ(&self) -> syn::Path { + self.base_typ.clone() + } +} diff --git a/crates/uniplate_derive/src/lib.rs b/crates/uniplate_derive/src/lib.rs index cd6854b50b..9eec4ab9c3 100644 --- a/crates/uniplate_derive/src/lib.rs +++ b/crates/uniplate_derive/src/lib.rs @@ -1,190 +1,252 @@ -use proc_macro::{self, TokenStream}; - -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Ident, Variant}; +mod ast; +mod prelude; +mod state; + +use std::collections::VecDeque; + +use prelude::*; +use quote::format_ident; +use syn::parse_macro_input; + +#[proc_macro_derive(Uniplate, attributes(uniplate, biplate))] +pub fn uniplate_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ast::DeriveInput); + //eprintln!("{:#?}",input.clone()); + let mut state: ParserState = ParserState::new(input.clone()); + + let mut out_tokens: Vec = Vec::new(); + out_tokens.push(quote! { + use std::borrow::Borrow as _; + }); + + while state.next_instance().is_some() { + out_tokens.push(match &state.current_instance { + Some(ast::InstanceMeta::Uniplate(_)) => derive_a_uniplate(&mut state), + Some(ast::InstanceMeta::Biplate(_)) => derive_a_biplate(&mut state), + _ => unreachable!(), + }); + } -use crate::utils::generate::{generate_field_clones, generate_field_fills, generate_field_idents}; + out_tokens.into_iter().collect::().into() +} -mod utils; +fn derive_a_uniplate(state: &mut ParserState) -> TokenStream2 { + let from = state.from.to_token_stream(); + let tokens: TokenStream2 = match state.data.clone() { + ast::Data::DataEnum(x) => _derive_a_enum_uniplate(state, x), + }; -/// Generate the full match pattern for a variant -fn generate_match_pattern(variant: &Variant, root_ident: &Ident) -> TokenStream2 { - let field_idents = generate_field_idents(&variant.fields); - let variant_ident = &variant.ident; + quote! { + impl ::uniplate::biplate::Uniplate for #from { + fn uniplate(&self) -> (::uniplate::Tree<#from>, Box) -> #from>) { + #tokens + } + } + } +} - if field_idents.is_empty() { - quote! { - #root_ident::#variant_ident +fn _derive_a_enum_uniplate(state: &mut ParserState, data: ast::DataEnum) -> TokenStream2 { + let mut variant_tokens = VecDeque::::new(); + for variant in data.variants { + if variant.fields.is_empty() { + let ident = variant.ident; + let enum_ident = state.data.ident(); + variant_tokens.push_back(quote! { + #enum_ident::#ident => { + (::uniplate::Tree::Zero,Box::new(|_| #enum_ident::#ident)) + }, + }); + } else { + let field_idents: Vec = variant + .fields + .iter() + .enumerate() + .map(|(i, _)| format_ident!("_f{}", i)) + .collect(); + let field_defs: Vec<_> = std::iter::zip(variant.fields.clone(), field_idents.clone()) + .map(|(field, ident)| _derive_for_field(state, field, ident)) + .collect(); + let children_def = _derive_children(state, &variant.fields); + let ctx_def = _derive_ctx(state, &variant.fields, &variant.ident); + let ident = variant.ident; + let enum_ident = state.data.ident(); + variant_tokens.push_back(quote! { + #enum_ident::#ident(#(#field_idents),*) => { + #(#field_defs)* + + #children_def + + #ctx_def + + (children,ctx) + }, + }); } - } else { - quote! { - #root_ident::#variant_ident(#(#field_idents,)*) + } + + let variant_tokens = variant_tokens.iter(); + quote! { + match self { + #(#variant_tokens)* } } } -/// Generate the code to get the children of a variant -fn generate_variant_children_match_arm(variant: &Variant, root_ident: &Ident) -> TokenStream2 { - let field_clones = generate_field_clones(&variant.fields, root_ident); +fn _derive_for_field( + state: &mut ParserState, + field: ast::Field, + ident: syn::Ident, +) -> TokenStream2 { + let children_ident = format_ident!("{}_children", ident); + let ctx_ident = format_ident!("{}_ctx", ident); - let match_pattern = generate_match_pattern(variant, root_ident); + let to_t = state.to.clone().expect("").to_token_stream(); - let clones = if field_clones.is_empty() { + if !state.walk_into_type(&field.typ) { + let copy_ident = format_ident!("{}_copy", ident); quote! { - Vec::new() + let #copy_ident = #ident.clone(); } } else { - quote! { - vec![#(#field_clones,)*].iter().flatten().cloned().collect::>() + match &field.typ { + // dereference the field + ast::Type::BoxedPlateable(x) => { + let from_t = x.inner_typ.to_token_stream(); + quote! { + let (#children_ident,#ctx_ident) = <#from_t as ::uniplate::biplate::Biplate<#to_t>>::biplate(#ident.borrow()); + } + } + ast::Type::Plateable(x) => { + let from_t = x.to_token_stream(); + quote! { + let (#children_ident,#ctx_ident) = <#from_t as ::uniplate::biplate::Biplate<#to_t>>::biplate(#ident); + } + } + ast::Type::Unplateable => { + let copy_ident = format_ident!("{}_copy", ident); + quote! { + let #copy_ident = #ident.clone(); + } + } } - }; + } +} - let mach_arm = quote! { - #match_pattern => { - #clones +fn _derive_children(state: &mut ParserState, fields: &[ast::Field]) -> TokenStream2 { + let mut subtrees: VecDeque = VecDeque::new(); + for (i, field) in fields.iter().enumerate() { + if !state.walk_into_type(&field.typ) { + subtrees.push_back(quote!(::uniplate::Tree::Zero)); + continue; } - }; + subtrees.push_back(match field.typ { + ast::Type::BoxedPlateable(_) => { + let children_ident = format_ident!("_f{}_children", i); + quote!(#children_ident) + } + ast::Type::Plateable(_) => { + let children_ident = format_ident!("_f{}_children", i); + quote!(#children_ident) + } + ast::Type::Unplateable => quote!(::uniplate::Tree::Zero), + }); + } - mach_arm + match subtrees.len() { + 0 => quote! {let children = ::uniplate::Tree::Zero;}, + _ => { + let subtrees = subtrees.iter(); + quote! {let children = ::uniplate::Tree::Many(::im::vector![#(#subtrees),*]);} + } + } } -/// Generate an implementation of `context` for a variant -fn generate_variant_context_match_arm(variant: &Variant, root_ident: &Ident) -> TokenStream2 { - let variant_ident = &variant.ident; - let children_ident = Ident::new("children", variant_ident.span()); - let field_fills = generate_field_fills(&variant.fields, root_ident, &children_ident); - let error_ident = format_ident!("UniplateError{}", root_ident); - let match_pattern = generate_match_pattern(variant, root_ident); +fn _derive_ctx( + state: &mut ParserState, + fields: &[ast::Field], + var_ident: &syn::Ident, +) -> TokenStream2 { + let field_ctxs: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, f)| { + if !state.walk_into_type(&f.typ) { + let ident = format_ident!("_f{}_copy", i); + quote! {#ident.clone()} + } else { + match &f.typ { + ast::Type::Unplateable => { + let ident = format_ident!("_f{}_copy", i); + quote! {#ident.clone()} + } - if field_fills.is_empty() { - quote! { - #match_pattern => { - Box::new(|_| Ok(#root_ident::#variant_ident)) - } - } - } else { - quote! { - #match_pattern => { - Box::new(|children| { - if (children.len() != self.children().len()) { - return Err(#error_ident::WrongNumberOfChildren(self.children().len(), children.len())); + ast::Type::Plateable(_) => { + let ctx_ident = format_ident!("_f{}_ctx", i); + quote! {#ctx_ident(x[#i].clone())} } - let mut #children_ident = children.clone(); - Ok(#root_ident::#variant_ident(#(#field_fills,)*)) - }) + ast::Type::BoxedPlateable(x) => { + let boxed_typ = x.box_typ.to_token_stream(); + let ctx_ident = format_ident!("_f{}_ctx", i); + quote! {#boxed_typ::new(#ctx_ident(x[#i].clone()))} + } + } } + }) + .collect(); + + let data_ident = state.data.ident(); + let typ = state.to.clone(); + match fields.len() { + 0 => { + quote! { + let ctx = Box::new(move |x: ::uniplate::Tree<#typ>| { + let ::uniplate::Tree::Zero = x else { panic!()}; + #data_ident::#var_ident + }); + } + } + _ => { + quote! { + let ctx = Box::new(move |x: ::uniplate::Tree<#typ>| { + let ::uniplate::Tree::Many(x) = x else { panic!()}; + #data_ident::#var_ident(#(#field_ctxs),*) + });} } } } -/// Derive the `Uniplate` trait for an arbitrary type -/// -/// # WARNING -/// -/// This is alpha code. It is not yet stable and some features are missing. -/// -/// ## What works? -/// -/// - Deriving `Uniplate` for enum types -/// - `Box` and `Vec` fields, including nested vectors -/// - Tuple fields, including nested tuples - e.g. `(Vec, (Box, i32))` -/// -/// ## What does not work? -/// -/// - Structs -/// - Unions -/// - Array fields -/// - Multiple type arguments - e.g. `MyType` -/// - Any complex type arguments, e.g. `MyType` -/// - Any collection type other than `Vec` -/// - Any box type other than `Box` -/// -/// # Usage -/// -/// This macro is intended to replace a hand-coded implementation of the `Uniplate` trait. -/// Example: -/// -/// ```rust -/// use uniplate_derive::Uniplate; -/// use uniplate::uniplate::Uniplate; -/// -/// #[derive(PartialEq, Eq, Debug, Clone, Uniplate)] -/// enum MyEnum { -/// A(Box), -/// B(Vec), -/// C(i32), -/// } -/// -/// let a = MyEnum::A(Box::new(MyEnum::C(42))); -/// let (children, context) = a.uniplate(); -/// assert_eq!(children, vec![MyEnum::C(42)]); -/// assert_eq!(context(vec![MyEnum::C(42)]).unwrap(), a); -/// ``` -/// -#[proc_macro_derive(Uniplate)] -pub fn derive(macro_input: TokenStream) -> TokenStream { - let input = parse_macro_input!(macro_input as DeriveInput); - let root_ident = &input.ident; - let data = &input.data; - - let children_impl: TokenStream2 = match data { - Data::Struct(_) => unimplemented!("Structs currently not supported"), // ToDo support structs - Data::Union(_) => unimplemented!("Unions currently not supported"), // ToDo support unions - Data::Enum(DataEnum { variants, .. }) => { - let match_arms: Vec = variants - .iter() - .map(|vt| generate_variant_children_match_arm(vt, root_ident)) - .collect::>(); +fn derive_a_biplate(state: &mut ParserState) -> TokenStream2 { + let from = state.from.base_typ.to_token_stream(); + let to = state.to.to_token_stream(); - let match_statement = quote! { - match self { - #(#match_arms)* - } - }; + if from.to_string() == to.to_string() { + return _derive_identity_biplate(from); + } - match_statement - } + let tokens: TokenStream2 = match state.data.clone() { + ast::Data::DataEnum(x) => _derive_a_enum_uniplate(state, x), }; - let context_impl = match data { - Data::Struct(_) => unimplemented!("Structs currently not supported"), - Data::Union(_) => unimplemented!("Unions currently not supported"), - Data::Enum(DataEnum { variants, .. }) => { - let match_arms: Vec = variants - .iter() - .map(|vt| generate_variant_context_match_arm(vt, root_ident)) - .collect::>(); - - let match_statement = quote! { - match self { - #(#match_arms)* - } - }; - - match_statement + quote! { + impl ::uniplate::biplate::Biplate<#to> for #from { + fn biplate(&self) -> (::uniplate::Tree<#to>, Box) -> #from>) { + #tokens + } } - }; - - let error_ident = format_ident!("UniplateError{}", root_ident); - - let output = quote! { - use uniplate::uniplate::UniplateError as #error_ident; - - impl Uniplate for #root_ident { - #[allow(unused_variables)] - fn uniplate(&self) -> (Vec<#root_ident>, Box) -> Result<#root_ident, #error_ident> + '_>) { - let context: Box) -> Result<#root_ident, #error_ident>> = #context_impl; - - let children: Vec<#root_ident> = #children_impl; + } +} - (children, context) +fn _derive_identity_biplate(typ: TokenStream2) -> TokenStream2 { + quote! { + impl ::uniplate::biplate::Biplate<#typ> for #typ{ + fn biplate(&self) -> (::uniplate::Tree<#typ>, Box) -> #typ>) { + let val = self.clone(); + (::uniplate::Tree::One(val.clone()),Box::new(move |x| { + let ::uniplate::Tree::One(x) = x else {todo!()}; + x + })) } } - }; - - // println!("Final macro output:\n{}", output.to_string()); - - output.into() + } } diff --git a/crates/uniplate_derive/src/prelude.rs b/crates/uniplate_derive/src/prelude.rs new file mode 100644 index 0000000000..456843f29a --- /dev/null +++ b/crates/uniplate_derive/src/prelude.rs @@ -0,0 +1,17 @@ +//! Common imports for all files in this project + +pub(crate) use crate::ast; +pub(crate) use crate::state::*; +pub(crate) use proc_macro::TokenStream; +pub(crate) use proc_macro2::Span; +pub(crate) use proc_macro2::TokenStream as TokenStream2; +pub(crate) use quote::quote; +pub(crate) use quote::ToTokens; + +pub(crate) use syn::braced; +pub(crate) use syn::parenthesized; +pub(crate) use syn::parse::Parse; +pub(crate) use syn::parse::ParseStream; +pub(crate) use syn::punctuated::Punctuated; +pub(crate) use syn::spanned::Spanned; +pub(crate) use syn::Token; diff --git a/crates/uniplate_derive/src/state.rs b/crates/uniplate_derive/src/state.rs new file mode 100644 index 0000000000..2d27701e3f --- /dev/null +++ b/crates/uniplate_derive/src/state.rs @@ -0,0 +1,91 @@ +//! Global(ish) Parser State variables + +use crate::prelude::*; +use std::collections::VecDeque; + +use self::ast::HasBaseType; + +/// State variables for biplate derive. +pub struct ParserState { + /// The type we are deriving Biplate on. + pub from: ast::PlateableType, + + /// The current target type. + pub to: Option, + + /// The data structure itself. + pub data: ast::Data, + + /// Information about the current instance being generated. + pub current_instance: Option, + + /// Instances left to generate. + pub instances_to_generate: VecDeque, +} + +impl ParserState { + pub fn new(inp: ast::DeriveInput) -> Self { + let data = inp.data; + let from: ast::PlateableType = data.clone().into(); + + let mut instances_to_generate: VecDeque = inp.instance_metadata.into(); + + // always generate Biplate + instances_to_generate.push_front(ast::InstanceMeta::Biplate(ast::BiplateInstanceMeta { + to: ast::Type::Plateable(from.clone()), + walk_into: Vec::new(), + })); + + Self { + current_instance: None, + to: None, + instances_to_generate, + from, + data, + } + } + + pub fn next_instance(&mut self) -> Option<()> { + let next_instance = self.instances_to_generate.pop_back(); + self.current_instance = next_instance; + + self.to = match &self.current_instance { + Some(ast::InstanceMeta::Uniplate(_)) => Some(self.from.clone()), + Some(ast::InstanceMeta::Biplate(b)) => { + let ast::Type::Plateable(t) = b.clone().to else { + // TODO: better error for this? + // probably should be in the parser not here! + unreachable!(); + }; + Some(t) + } + None => None, + }; + + if self.current_instance.is_none() { + return None; + }; + + Some(()) + } + + /// Checks if a type can be walked into or not + + /// This acts similarly to ast::InstanceMeta::walk_into_type but also considers the + /// current to and from type as walkable. + pub fn walk_into_type(&self, typ: &ast::Type) -> bool { + let Some(base_typ) = typ.base_typ() else { + return false; + }; + + if base_typ == self.to.clone().expect("").base_typ() { + return true; + }; + + if base_typ == self.from.base_typ() { + return true; + }; + + self.current_instance.clone().expect("").walk_into_type(typ) + } +} diff --git a/crates/uniplate_derive/src/utils/generate.rs b/crates/uniplate_derive/src/utils/generate.rs deleted file mode 100644 index ecaa539779..0000000000 --- a/crates/uniplate_derive/src/utils/generate.rs +++ /dev/null @@ -1,185 +0,0 @@ -use proc_macro2::{Ident, Literal, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; -use syn::spanned::Spanned; -use syn::{Field, Fields}; - -use crate::utils::parse::{check_field_type, parse_field_type, UniplateField}; - -/// Generate the code to fill a field in a variant -fn get_fill( - ft: &UniplateField, - exprs_ident: &Ident, - field_ident: &TokenStream2, - root_ident: &Ident, -) -> TokenStream2 { - if check_field_type(ft, root_ident) { - // If the field or at least one of its children is a type we want to fill - match ft { - UniplateField::Identifier(_) => { - return quote! { - #exprs_ident.remove(0) // If it is an identifier, take the next child from the list - }; - } - UniplateField::Box(_, subfield) => { - let sf = subfield.as_ref(); - let sf_fill = get_fill(sf, exprs_ident, field_ident, root_ident); - return quote! { - Box::new(#sf_fill) // If it is a box, generate the fill for the inner type and box it - }; - } - UniplateField::Vector(_, subfield) => { - let sf = subfield.as_ref(); - let sf_fill = get_fill(sf, exprs_ident, field_ident, root_ident); - return quote! { // The size is not known at compile time, so generate a loop to fill the vector (using the appropriate fill for the inner type) - { - let mut elems: Vec<_> = Vec::new(); - for i in 0..#field_ident.len() { // The length of vectors must not change, so we can use the length of the field to determine how many children to take - elems.push(#sf_fill) - } - elems - } - }; - } - UniplateField::Tuple(_, sfs) => { - let mut sf_fills: Vec = Vec::new(); - - for (i, sf) in sfs.iter().enumerate() { - // Recursively generate the fill for each field in the tuple - let i_literal = Literal::usize_unsuffixed(i); - let sf_ident = quote! { - #field_ident.#i_literal - }; - sf_fills.push(get_fill(sf, exprs_ident, &sf_ident, root_ident)); - } - - return quote! { - (#(#sf_fills,)*) // Wrap the fills in a tuple - }; - } - UniplateField::Array(_, _, _) => { - unimplemented!("Arrays not currently supported") // ToDo support arrays - } - UniplateField::Unknown(_) => {} - } - } - - quote! { - #field_ident.clone() // If the field is not a type we want to fill, just keep it - } -} - -/// Generate the code to clone a field in a variant -fn get_clone( - ft: &UniplateField, - field_ident: TokenStream2, - root_ident: &Ident, -) -> Option { - if check_field_type(ft, root_ident) { - // If the field or at least one of its children is a type we want to clone - match ft { - UniplateField::Identifier(_) => { - return Some(quote! { - vec![#field_ident.clone()] // If it is an identifier, clone it. We still need to wrap it in a vec to use .flatten() on the final list. - }); - } - UniplateField::Box(_, inner) => { - let sf = inner.as_ref(); - let box_clone = quote! { // Generate the prefix for getting the inner type out of the box - #field_ident.as_ref().clone() - }; - return get_clone(sf, box_clone, root_ident); // Then generate the clone for the inner type - } - UniplateField::Vector(_, inner) => { - let sf = inner.as_ref(); - - let sf_ident = Ident::new("sf", sf.span()).into_token_stream(); // Identity for the subfields - let sf_clone = get_clone(sf, sf_ident, root_ident); // Clone for the subfields - - return Some(quote! { - #field_ident.iter().flat_map(|sf| #sf_clone).collect::>() // If it is a vector, generate the clone for the inner type and flatten the list - }); - } - UniplateField::Tuple(_, sfs) => { - let mut sf_clones: Vec = Vec::new(); - - for (i, sf) in sfs.iter().enumerate() { - // Recursively generate the clone for each field in the tuple - let i_literal = Literal::usize_unsuffixed(i); - let sf_ident = quote! { - #field_ident.#i_literal - }; - let sf_clone = get_clone(sf, sf_ident, root_ident); - match sf_clone { - None => {} - Some(sfc) => sf_clones.push(sfc), - } - } - - return Some(quote! { // Clone the subfields into a vec and flatten - vec![#(#sf_clones,)*].iter().flatten().cloned().collect::>() - }); - } - UniplateField::Array(_, _, _) => { - // ToDo support arrays - unimplemented!("Arrays not currently supported") - } - UniplateField::Unknown(_) => {} // Ignore unknown types - } - } - - None // If the field is not a type we want to clone, return None -} - -/// Helper function to get the name of a field - if it has no name, use `field{idx}` -fn get_field_name(field: &Field, idx: usize) -> String { - match &field.ident { - None => format!("field{}", idx), - Some(ident) => ident.to_string(), - } -} - -/// Generate the code to match the fields of a variant -pub fn generate_field_idents(fields: &Fields) -> Vec { - return fields - .iter() - .enumerate() - .map(|(idx, field)| { - let field_name = get_field_name(field, idx); - Ident::new(&field_name, field.ident.span()).into_token_stream() - }) - .collect(); -} - -/// Generate the code to clone the fields of a variant -pub fn generate_field_clones(fields: &Fields, root_ident: &Ident) -> Vec { - return fields - .iter() - .enumerate() - .filter_map(|(idx, field)| { - let field_name = get_field_name(field, idx); - let field_type = parse_field_type(&field.ty); - let field_ident = Ident::new(&field_name, field.ident.span()).into_token_stream(); - - get_clone(&field_type, field_ident, root_ident) - }) - .collect(); -} - -/// Generate the code to fill the fields of a variant -pub fn generate_field_fills( - fields: &Fields, - root_ident: &Ident, - exprs_ident: &Ident, -) -> Vec { - return fields - .iter() - .enumerate() - .map(|(idx, field)| { - let field_name = get_field_name(field, idx); - let field_type = parse_field_type(&field.ty); - let field_ident = Ident::new(&field_name, field.ident.span()).into_token_stream(); - - get_fill(&field_type, exprs_ident, &field_ident, root_ident) - }) - .collect(); -} diff --git a/crates/uniplate_derive/src/utils/mod.rs b/crates/uniplate_derive/src/utils/mod.rs deleted file mode 100644 index 3f14f20a47..0000000000 --- a/crates/uniplate_derive/src/utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod generate; -pub mod parse; diff --git a/crates/uniplate_derive/src/utils/parse.rs b/crates/uniplate_derive/src/utils/parse.rs deleted file mode 100644 index 5270fbe756..0000000000 --- a/crates/uniplate_derive/src/utils/parse.rs +++ /dev/null @@ -1,133 +0,0 @@ -use proc_macro2::{Ident, Span}; -use syn::spanned::Spanned; -use syn::{Expr, GenericArgument, PathArguments, PathSegment, Type}; - -/// Represents an error produced during parsing a type argument (e.g. `::`) -pub enum ParseTypeArgumentError { - NoTypeArguments, - EmptyTypeArguments, - MultipleTypeArguments, - TypeArgumentNotAType, - TypeArgumentValueNotPath, - TypeArgumentEmptyPath, -} - -/// Represents a field in a tree-like structure. Used for deriving the uniplate implementation. -#[derive(Debug)] -pub enum UniplateField { - /// Any other valid identifier - Identifier(Ident), - /// A field consisting of a Box - Box(Span, Box), - /// A field consisting of a Vec - Vector(Span, Box), - /// A tuple of multiple fields (e.g. `(Box, i32)`) - Tuple(Span, Vec), - /// An array field. ToDo: currently not supported. - Array(Span, Box, Expr), - /// A field that could not be parsed - Unknown(Span), -} - -impl UniplateField { - /// Get the span corresponding to this field - pub fn span(&self) -> Span { - match self { - UniplateField::Identifier(idnt) => idnt.span(), - UniplateField::Box(spn, _) => *spn, - UniplateField::Vector(spn, _) => *spn, - UniplateField::Tuple(spn, _) => *spn, - UniplateField::Array(spn, _, _) => *spn, - UniplateField::Unknown(spn) => *spn, - } - } -} - -/// Parse a type argument from a path segment (e.g. `T` from `Box`) -fn parse_type_argument(seg_args: &PathArguments) -> Result<&PathSegment, ParseTypeArgumentError> { - match seg_args { - PathArguments::AngleBracketed(type_args) => { - if type_args.args.len() > 1 { - // ToDo: discuss - can and should we support multiple type arguments? - return Err(ParseTypeArgumentError::MultipleTypeArguments); - } - - match type_args.args.last() { - None => Err(ParseTypeArgumentError::EmptyTypeArguments), - Some(arg) => match arg { - GenericArgument::Type(tp) => match tp { - Type::Path(pth) => match pth.path.segments.last() { - Some(seg) => Ok(seg), - None => Err(ParseTypeArgumentError::TypeArgumentEmptyPath), - }, - _ => Err(ParseTypeArgumentError::TypeArgumentValueNotPath), - }, - _ => Err(ParseTypeArgumentError::TypeArgumentNotAType), - }, - } - } - _ => Err(ParseTypeArgumentError::NoTypeArguments), - } -} - -/// Parse a field type into a `UniplateField` -pub fn parse_field_type(field_type: &Type) -> UniplateField { - /// Helper function to parse a path segment into a `UniplateField` - fn parse_type(seg: &PathSegment) -> UniplateField { - let ident = &seg.ident; - let span = ident.span(); - let args = &seg.arguments; - - let box_ident = &Ident::new("Box", span); - let vec_ident = &Ident::new("Vec", span); // ToDo: support other collection types - - if ident.eq(box_ident) { - match parse_type_argument(args) { - Ok(inner_seg) => UniplateField::Box(seg.span(), Box::new(parse_type(inner_seg))), - Err(_) => UniplateField::Unknown(ident.span()), - } - } else if ident.eq(vec_ident) { - match parse_type_argument(args) { - Ok(inner_seg) => UniplateField::Vector(seg.span(), Box::new(parse_type(inner_seg))), - Err(_) => UniplateField::Unknown(ident.span()), - } - } else { - UniplateField::Identifier(ident.clone()) - } - } - - match field_type { - Type::Path(path) => match path.path.segments.last() { - None => UniplateField::Unknown(path.span()), - Some(seg) => parse_type(seg), - }, - Type::Tuple(tpl) => { - UniplateField::Tuple(tpl.span(), tpl.elems.iter().map(parse_field_type).collect()) - } - Type::Array(arr) => UniplateField::Array( - arr.span(), - Box::new(parse_field_type(arr.elem.as_ref())), - arr.len.clone(), - ), - _ => UniplateField::Unknown(field_type.span()), // ToDo discuss - Can we support any of: BareFn, Group, ImplTrait, Infer, Macro, Never, Paren, Ptr, Reference, TraitObject, Verbatim - } -} - -/// Check if a field type is equal to a given identifier. Used to check if a field is an instance of the root type. -pub fn check_field_type(ft: &UniplateField, root_ident: &Ident) -> bool { - match ft { - UniplateField::Identifier(ident) => ident.eq(root_ident), - UniplateField::Box(_, subfield) => check_field_type(subfield.as_ref(), root_ident), - UniplateField::Vector(_, subfield) => check_field_type(subfield.as_ref(), root_ident), - UniplateField::Tuple(_, subfields) => { - for sft in subfields { - if check_field_type(sft, root_ident) { - return true; - } - } - false - } - UniplateField::Array(_, arr_type, _) => check_field_type(arr_type.as_ref(), root_ident), - UniplateField::Unknown(_) => false, - } -} diff --git a/crates/uniplate_derive/tests/macro_tests.rs b/crates/uniplate_derive/tests/macro_tests.rs deleted file mode 100644 index 4dd3842d4f..0000000000 --- a/crates/uniplate_derive/tests/macro_tests.rs +++ /dev/null @@ -1,244 +0,0 @@ -use uniplate::uniplate::Uniplate; -use uniplate_derive::Uniplate; - -#[derive(Clone, Debug, PartialEq, Eq, Uniplate)] -enum TestEnum { - A(i32), - B(Box), - C(Vec), - D(bool, Box), - E(Box, Box), - F((Box, Box)), - G((Box, (Box, i32))), - H(Vec>), - I(Vec, i32, Vec), -} - -#[test] -fn increase_number_of_children() { - let c = TestEnum::C(vec![TestEnum::A(42)]); - let context = c.uniplate().1; - assert_eq!( - context(vec![TestEnum::A(42), TestEnum::A(42)]), - Err(uniplate::uniplate::UniplateError::WrongNumberOfChildren( - 1, 2 - )) - ); -} - -#[test] -fn decrease_number_of_children() { - let c = TestEnum::C(vec![TestEnum::A(42)]); - let context = c.uniplate().1; - assert_eq!( - context(vec![]), - Err(uniplate::uniplate::UniplateError::WrongNumberOfChildren( - 1, 0 - )) - ); -} - -#[test] -fn derive_context_empty() { - let a = TestEnum::A(42); - let context = a.uniplate().1; - assert_eq!(context(vec![]).unwrap(), a) -} - -#[test] -fn derive_context_box() { - let a = TestEnum::A(42); - let b = TestEnum::B(Box::new(a.clone())); - let context = b.uniplate().1; - assert_eq!(context(vec![a.clone()]).unwrap(), b); -} - -#[test] -fn derive_context_vec() { - let a = TestEnum::A(1); - let b = TestEnum::B(Box::new(TestEnum::A(2))); - let c = TestEnum::C(vec![a.clone(), b.clone()]); - let context = c.uniplate().1; - assert_eq!(context(vec![a.clone(), b.clone()]).unwrap(), c); -} - -#[test] -fn derive_context_two() { - let d = TestEnum::D(true, Box::new(TestEnum::A(42))); - let context = d.uniplate().1; - assert_eq!(context(vec![TestEnum::A(42)]).unwrap(), d); -} - -#[test] -fn derive_context_tuple() { - let e = TestEnum::F((Box::new(TestEnum::A(1)), Box::new(TestEnum::A(2)))); - let context = e.uniplate().1; - assert_eq!(context(vec![TestEnum::A(1), TestEnum::A(2)]).unwrap(), e); -} - -#[test] -fn derive_context_different_variants() { - let f = TestEnum::E( - Box::new(TestEnum::A(1)), - Box::new(TestEnum::B(Box::new(TestEnum::A(2)))), - ); - let context = f.uniplate().1; - assert_eq!( - context(vec![TestEnum::A(1), TestEnum::B(Box::new(TestEnum::A(2)))]).unwrap(), - f - ); -} - -#[test] -fn derive_context_nested_tuples() { - let g = TestEnum::G((Box::new(TestEnum::A(1)), (Box::new(TestEnum::A(2)), 42))); - let context = g.uniplate().1; - assert_eq!(context(vec![TestEnum::A(1), TestEnum::A(2)]).unwrap(), g); -} - -#[test] -fn derive_context_nested_vectors() { - let h = TestEnum::H(vec![ - vec![TestEnum::A(1), TestEnum::A(2)], - vec![TestEnum::A(3), TestEnum::A(4)], - ]); - let context = h.uniplate().1; - assert_eq!( - context(vec![ - TestEnum::A(1), - TestEnum::A(2), - TestEnum::A(3), - TestEnum::A(4) - ]) - .unwrap(), - h - ); -} - -#[test] -fn derive_context_multiple_vecs() { - let i = TestEnum::I( - vec![TestEnum::A(1), TestEnum::A(2)], - 42, - vec![TestEnum::A(3), TestEnum::A(4)], - ); - let context = i.uniplate().1; - assert_eq!( - context(vec![ - TestEnum::A(1), - TestEnum::A(2), - TestEnum::A(3), - TestEnum::A(4) - ]) - .unwrap(), - i - ); -} - -#[test] -fn box_change_child() { - let b = TestEnum::B(Box::new(TestEnum::A(1))); - let context = b.uniplate().1; - assert_eq!( - context(vec![TestEnum::C(vec![TestEnum::A(41), TestEnum::A(42)])]).unwrap(), - TestEnum::B(Box::new(TestEnum::C(vec![ - TestEnum::A(41), - TestEnum::A(42) - ]))) - ); -} - -#[test] -fn derive_children_empty() { - let a = TestEnum::A(42); - let children = a.uniplate().0; - assert_eq!(children, vec![]); -} - -#[test] -fn derive_children_box() { - let b = TestEnum::B(Box::new(TestEnum::A(42))); - let children = b.uniplate().0; - assert_eq!(children, vec![TestEnum::A(42)]); -} - -#[test] -fn derive_children_vec() { - let c = TestEnum::C(vec![TestEnum::A(1), TestEnum::B(Box::new(TestEnum::A(2)))]); - let children = c.uniplate().0; - assert_eq!( - children, - vec![TestEnum::A(1), TestEnum::B(Box::new(TestEnum::A(2))),] - ); -} - -#[test] -fn derive_children_two() { - let d = TestEnum::D(true, Box::new(TestEnum::A(42))); - let children = d.uniplate().0; - assert_eq!(children, vec![TestEnum::A(42)]); -} - -#[test] -fn derive_children_tuple() { - let e = TestEnum::F((Box::new(TestEnum::A(1)), Box::new(TestEnum::A(2)))); - let children = e.uniplate().0; - assert_eq!(children, vec![TestEnum::A(1), TestEnum::A(2),]); -} - -#[test] -fn derive_children_different_variants() { - let f = TestEnum::E( - Box::new(TestEnum::A(1)), - Box::new(TestEnum::B(Box::new(TestEnum::A(2)))), - ); - let children = f.uniplate().0; - assert_eq!( - children, - vec![TestEnum::A(1), TestEnum::B(Box::new(TestEnum::A(2)))] - ); -} - -#[test] -fn derive_children_nested_tuples() { - let g = TestEnum::G((Box::new(TestEnum::A(1)), (Box::new(TestEnum::A(2)), 42))); - let children = g.uniplate().0; - assert_eq!(children, vec![TestEnum::A(1), TestEnum::A(2)]) -} - -#[test] -fn derive_children_nested_vectors() { - let h = TestEnum::H(vec![ - vec![TestEnum::A(1), TestEnum::A(2)], - vec![TestEnum::A(3), TestEnum::A(4)], - ]); - let children = h.uniplate().0; - assert_eq!( - children, - vec![ - TestEnum::A(1), - TestEnum::A(2), - TestEnum::A(3), - TestEnum::A(4) - ] - ) -} - -#[test] -fn derive_children_multiple_vecs() { - let i = TestEnum::I( - vec![TestEnum::A(1), TestEnum::A(2)], - 42, - vec![TestEnum::A(3), TestEnum::A(4)], - ); - let children = i.uniplate().0; - assert_eq!( - children, - vec![ - TestEnum::A(1), - TestEnum::A(2), - TestEnum::A(3), - TestEnum::A(4) - ] - ); -}