diff --git a/Cargo.lock b/Cargo.lock index 049f875a..7be5e7d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,18 +19,27 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "async-graphql-parser" @@ -103,25 +112,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.7.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "block-padding", - "byte-tools", - "byteorder", "generic-array", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - [[package]] name = "bstr" version = "0.2.17" @@ -143,31 +140,25 @@ dependencies = [ ] [[package]] -name = "byte-tools" -version = "0.3.1" +name = "bumpalo" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" dependencies = [ "serde", ] [[package]] name = "camino" -version = "1.0.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" dependencies = [ "serde", ] @@ -199,7 +190,6 @@ dependencies = [ "ignore", "log", "ron 0.7.1", - "rustdoc-types", "semver", "serde", "serde_json", @@ -207,6 +197,7 @@ dependencies = [ "termcolor_output", "toml_edit", "trustfall_core", + "trustfall_rustdoc", ] [[package]] @@ -239,23 +230,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "serde", "time", + "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "3.2.10" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c5a7f9997be616e47f0577ee38c91decb33392c5be4866494f34cdf329a9aa" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -291,9 +284,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -321,6 +314,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + [[package]] name = "crates-index" version = "0.18.9" @@ -387,12 +395,23 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.8.1" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer", + "crypto-common", ] [[package]] @@ -403,15 +422,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" - -[[package]] -name = "fake-simd" -version = "0.1.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "fnv" @@ -421,21 +434,21 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "generic-array" -version = "0.12.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", + "version_check", ] [[package]] @@ -507,9 +520,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.1" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66d0c1b6e3abfd1e72818798925e16e02ed77e1b47f6c25a95a23b377ee4299" +checksum = "56b224eaa4987c03c30b251de7ef0c15a6a59f34222905850dbc3026dfb24d5f" dependencies = [ "log", "pest", @@ -521,9 +534,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -573,13 +586,25 @@ dependencies = [ "uuid", ] +[[package]] +name = "iana-time-zone" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -615,28 +640,37 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -645,9 +679,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libgit2-sys" @@ -689,12 +723,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" version = "2.5.0" @@ -712,9 +740,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -759,15 +787,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "opaque-debug" -version = "0.2.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "openssl-probe" @@ -777,9 +799,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", @@ -790,39 +812,40 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "os_type" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3df761f6470298359f84fcfb60d86db02acc22c251c37265c07a3d1057d2389" +checksum = "ccc42dd7c59785b0a52b68564cd0ca072a05eec582e47c69a04d608eb1d06edc" dependencies = [ "regex", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1" dependencies = [ "pest", "pest_generator", @@ -830,9 +853,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c" dependencies = [ "pest", "pest_meta", @@ -843,13 +866,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1", + "sha1", ] [[package]] @@ -890,18 +913,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -981,6 +1004,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustdoc-types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a260c376ebec8b6fcd30f518b253772873c5dd8564b45e384aad8a79c62aa6" +dependencies = [ + "serde", +] + [[package]] name = "rustdoc-types" version = "0.17.0" @@ -990,11 +1022,20 @@ dependencies = [ "serde", ] +[[package]] +name = "rustdoc-types" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7745b68b43d89a088d2d270be09f54f948e587fc540cb15e3d3b75cb327a01c3" +dependencies = [ + "serde", +] + [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "same-file" @@ -1013,27 +1054,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.139" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -1042,9 +1083,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -1052,15 +1093,14 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.8.2" +name = "sha1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "block-buffer", + "cfg-if", + "cpufeatures", "digest", - "fake-simd", - "opaque-debug", ] [[package]] @@ -1104,9 +1144,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ "proc-macro2", "quote", @@ -1150,24 +1190,24 @@ checksum = "f34dde0bb841eb3762b42bdff8db11bbdbc0a3bd7b32012955f5ce1d081f86c1" [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -1229,6 +1269,36 @@ dependencies = [ "itertools", ] +[[package]] +name = "trustfall-rustdoc-adapter" +version = "16.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95052afbacf21557e7d36b7ff4c84f6a05c30fd7e3b28517ebfebc2b57422a7" +dependencies = [ + "rustdoc-types 0.12.0", + "trustfall_core", +] + +[[package]] +name = "trustfall-rustdoc-adapter" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c17fe4fb18f83cbfd43c02cfd348fb7c0a09329487a980455cebbd68a1ba81" +dependencies = [ + "rustdoc-types 0.17.0", + "trustfall_core", +] + +[[package]] +name = "trustfall-rustdoc-adapter" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc9f18fc39b106f7882ed1fe05045c5185bbdd4962700ef91d8daf09df67b2e" +dependencies = [ + "rustdoc-types 0.18.0", + "trustfall_core", +] + [[package]] name = "trustfall_core" version = "0.0.4" @@ -1264,6 +1334,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "trustfall_rustdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad02451065ebd9e5840407a3f3783f6e5b4a5f419861b9e40f46d006fc5b50c2" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "trustfall-rustdoc-adapter 16.0.1", + "trustfall-rustdoc-adapter 21.0.1", + "trustfall-rustdoc-adapter 22.0.0", + "trustfall_core", +] + [[package]] name = "typenum" version = "1.15.0" @@ -1272,9 +1357,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-bidi" @@ -1284,28 +1369,27 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -1353,6 +1437,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index b800062e..1fd15662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = ["command-line-utilities", "development-tools::cargo-plugins"] [dependencies] trustfall_core = "0.0.4" -rustdoc-types = "0.17.0" +trustfall_rustdoc = { version = "0.3.0", features = ["v16", "v21", "v22"] } clap = { version = "3.2.8", features = ["derive", "cargo"] } serde_json = "1.0.82" anyhow = "1.0.58" diff --git a/src/adapter.rs b/src/adapter.rs deleted file mode 100644 index 978eebf7..00000000 --- a/src/adapter.rs +++ /dev/null @@ -1,1330 +0,0 @@ -use std::{collections::BTreeSet, sync::Arc}; - -use rustdoc_types::{ - Crate, Enum, Function, Id, Impl, Item, ItemEnum, Method, Path, Span, Struct, Trait, Type, - Variant, -}; -use trustfall_core::{ - interpreter::{Adapter, DataContext, InterpretedQuery}, - ir::{EdgeParameters, Eid, FieldValue, Vid}, - schema::Schema, -}; - -use crate::indexed_crate::IndexedCrate; - -#[non_exhaustive] -pub struct RustdocAdapter<'a> { - current_crate: &'a IndexedCrate<'a>, - previous_crate: Option<&'a IndexedCrate<'a>>, -} - -impl<'a> RustdocAdapter<'a> { - pub fn new( - current_crate: &'a IndexedCrate<'a>, - previous_crate: Option<&'a IndexedCrate<'a>>, - ) -> Self { - Self { - current_crate, - previous_crate, - } - } - - pub fn schema() -> Schema { - Schema::parse(include_str!("rustdoc_schema.graphql")).expect("schema not valid") - } -} - -#[non_exhaustive] -#[derive(Debug, Clone, Copy)] -pub enum Origin { - CurrentCrate, - PreviousCrate, -} - -impl Origin { - fn make_item_token<'a>(&self, item: &'a Item) -> Token<'a> { - Token { - origin: *self, - kind: item.into(), - } - } - - fn make_span_token<'a>(&self, span: &'a Span) -> Token<'a> { - Token { - origin: *self, - kind: span.into(), - } - } - - fn make_path_token<'a>(&self, path: &'a [String]) -> Token<'a> { - Token { - origin: *self, - kind: TokenKind::Path(path), - } - } - - fn make_importable_path_token<'a>(&self, importable_path: Vec<&'a str>) -> Token<'a> { - Token { - origin: *self, - kind: TokenKind::ImportablePath(importable_path), - } - } - - fn make_raw_type_token<'a>(&self, raw_type: &'a rustdoc_types::Type) -> Token<'a> { - Token { - origin: *self, - kind: TokenKind::RawType(raw_type), - } - } - - fn make_attribute_token<'a>(&self, attr: &'a str) -> Token<'a> { - Token { - origin: *self, - kind: TokenKind::Attribute(attr), - } - } - - fn make_implemented_trait_token<'a>( - &self, - path: &'a rustdoc_types::Path, - trait_def: &'a Item, - ) -> Token<'a> { - Token { - origin: *self, - kind: TokenKind::ImplementedTrait(path, trait_def), - } - } -} - -#[non_exhaustive] -#[derive(Debug, Clone)] -pub struct Token<'a> { - origin: Origin, - kind: TokenKind<'a>, -} - -#[non_exhaustive] -#[derive(Debug, Clone)] -pub enum TokenKind<'a> { - CrateDiff((&'a IndexedCrate<'a>, &'a IndexedCrate<'a>)), - Crate(&'a IndexedCrate<'a>), - Item(&'a Item), - Span(&'a Span), - Path(&'a [String]), - ImportablePath(Vec<&'a str>), - RawType(&'a Type), - Attribute(&'a str), - ImplementedTrait(&'a Path, &'a Item), -} - -#[allow(dead_code)] -impl<'a> Token<'a> { - fn new_crate(origin: Origin, crate_: &'a IndexedCrate<'a>) -> Self { - Self { - origin, - kind: TokenKind::Crate(crate_), - } - } - - /// The name of the actual runtime type of this token, - /// intended to fulfill resolution requests for the __typename property. - #[inline] - fn typename(&self) -> &'static str { - match self.kind { - TokenKind::Item(item) => match &item.inner { - rustdoc_types::ItemEnum::Struct(..) => "Struct", - rustdoc_types::ItemEnum::Enum(..) => "Enum", - rustdoc_types::ItemEnum::Function(..) => "Function", - rustdoc_types::ItemEnum::Method(..) => "Method", - rustdoc_types::ItemEnum::Variant(Variant::Plain(..)) => "PlainVariant", - rustdoc_types::ItemEnum::Variant(Variant::Tuple(..)) => "TupleVariant", - rustdoc_types::ItemEnum::Variant(Variant::Struct { .. }) => "StructVariant", - rustdoc_types::ItemEnum::StructField(..) => "StructField", - rustdoc_types::ItemEnum::Impl(..) => "Impl", - rustdoc_types::ItemEnum::Trait(..) => "Trait", - _ => unreachable!("unexpected item.inner for item: {item:?}"), - }, - TokenKind::Span(..) => "Span", - TokenKind::Path(..) => "Path", - TokenKind::ImportablePath(..) => "ImportablePath", - TokenKind::Crate(..) => "Crate", - TokenKind::CrateDiff(..) => "CrateDiff", - TokenKind::Attribute(..) => "Attribute", - TokenKind::ImplementedTrait(..) => "ImplementedTrait", - TokenKind::RawType(ty) => match ty { - rustdoc_types::Type::ResolvedPath { .. } => "ResolvedPathType", - rustdoc_types::Type::Primitive(..) => "PrimitiveType", - _ => "OtherType", - }, - } - } - - fn as_crate_diff(&self) -> Option<(&'a IndexedCrate<'a>, &'a IndexedCrate<'a>)> { - match &self.kind { - TokenKind::CrateDiff(tuple) => Some(*tuple), - _ => None, - } - } - - fn as_indexed_crate(&self) -> Option<&'a IndexedCrate<'a>> { - match self.kind { - TokenKind::Crate(c) => Some(c), - _ => None, - } - } - - fn as_crate(&self) -> Option<&'a Crate> { - self.as_indexed_crate().map(|c| c.inner) - } - - fn as_item(&self) -> Option<&'a Item> { - match self.kind { - TokenKind::Item(item) => Some(item), - _ => None, - } - } - - fn as_struct_item(&self) -> Option<(&'a Item, &'a Struct)> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Struct(s) => Some((item, s)), - _ => None, - }) - } - - fn as_struct_field_item(&self) -> Option<(&'a Item, &'a Type)> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::StructField(s) => Some((item, s)), - _ => None, - }) - } - - fn as_span(&self) -> Option<&'a Span> { - match self.kind { - TokenKind::Span(s) => Some(s), - _ => None, - } - } - - fn as_enum(&self) -> Option<&'a Enum> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Enum(e) => Some(e), - _ => None, - }) - } - - fn as_trait(&self) -> Option<&'a Trait> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Trait(t) => Some(t), - _ => None, - }) - } - - fn as_variant(&self) -> Option<&'a Variant> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Variant(v) => Some(v), - _ => None, - }) - } - - fn as_path(&self) -> Option<&'a [String]> { - match &self.kind { - TokenKind::Path(path) => Some(*path), - _ => None, - } - } - - fn as_importable_path(&self) -> Option<&'_ Vec<&'a str>> { - match &self.kind { - TokenKind::ImportablePath(path) => Some(path), - _ => None, - } - } - - fn as_function(&self) -> Option<&'a Function> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Function(func) => Some(func), - _ => None, - }) - } - - fn as_method(&self) -> Option<&'a Method> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Method(func) => Some(func), - _ => None, - }) - } - - fn as_impl(&self) -> Option<&'a Impl> { - self.as_item().and_then(|item| match &item.inner { - rustdoc_types::ItemEnum::Impl(x) => Some(x), - _ => None, - }) - } - - fn as_attribute(&self) -> Option<&'a str> { - match &self.kind { - TokenKind::Attribute(attr) => Some(*attr), - _ => None, - } - } - - fn as_raw_type(&self) -> Option<&'a rustdoc_types::Type> { - match &self.kind { - TokenKind::RawType(ty) => Some(*ty), - _ => None, - } - } - - fn as_implemented_trait(&self) -> Option<(&'a rustdoc_types::Path, &'a Item)> { - match &self.kind { - TokenKind::ImplementedTrait(path, trait_item) => Some((*path, *trait_item)), - _ => None, - } - } -} - -impl<'a> From<&'a Item> for TokenKind<'a> { - fn from(item: &'a Item) -> Self { - Self::Item(item) - } -} - -impl<'a> From<&'a IndexedCrate<'a>> for TokenKind<'a> { - fn from(c: &'a IndexedCrate<'a>) -> Self { - Self::Crate(c) - } -} - -impl<'a> From<&'a Span> for TokenKind<'a> { - fn from(s: &'a Span) -> Self { - Self::Span(s) - } -} - -fn get_crate_property(crate_token: &Token, field_name: &str) -> FieldValue { - let crate_item = crate_token.as_crate().expect("token was not a Crate"); - match field_name { - "root" => (&crate_item.root.0).into(), - "crate_version" => (&crate_item.crate_version).into(), - "includes_private" => crate_item.includes_private.into(), - "format_version" => crate_item.format_version.into(), - _ => unreachable!("Crate property {field_name}"), - } -} - -fn get_item_property(item_token: &Token, field_name: &str) -> FieldValue { - let item = item_token.as_item().expect("token was not an Item"); - match field_name { - "id" => (&item.id.0).into(), - "crate_id" => (&item.crate_id).into(), - "name" => (&item.name).into(), - "docs" => (&item.docs).into(), - "attrs" => item.attrs.clone().into(), - "visibility_limit" => match &item.visibility { - rustdoc_types::Visibility::Public => "public".into(), - rustdoc_types::Visibility::Default => "default".into(), - rustdoc_types::Visibility::Crate => "crate".into(), - rustdoc_types::Visibility::Restricted { parent: _, path } => { - format!("restricted ({path})").into() - } - }, - _ => unreachable!("Item property {field_name}"), - } -} - -fn get_struct_property(item_token: &Token, field_name: &str) -> FieldValue { - let (_, struct_item) = item_token.as_struct_item().expect("token was not a Struct"); - match field_name { - "struct_type" => match struct_item.kind { - rustdoc_types::StructKind::Plain { .. } => "plain", - rustdoc_types::StructKind::Tuple(..) => "tuple", - rustdoc_types::StructKind::Unit => "unit", - } - .into(), - "fields_stripped" => match struct_item.kind { - rustdoc_types::StructKind::Plain { - fields_stripped, .. - } => fields_stripped.into(), - _ => FieldValue::Null, - }, - _ => unreachable!("Struct property {field_name}"), - } -} - -fn get_span_property(item_token: &Token, field_name: &str) -> FieldValue { - let span = item_token.as_span().expect("token was not a Span"); - match field_name { - "filename" => span - .filename - .to_str() - .expect("non-representable path") - .into(), - "begin_line" => (span.begin.0 as u64).into(), - "begin_column" => (span.begin.1 as u64).into(), - "end_line" => (span.end.0 as u64).into(), - "end_column" => (span.end.1 as u64).into(), - _ => unreachable!("Span property {field_name}"), - } -} - -fn get_enum_property(item_token: &Token, field_name: &str) -> FieldValue { - let enum_item = item_token.as_enum().expect("token was not an Enum"); - match field_name { - "variants_stripped" => enum_item.variants_stripped.into(), - _ => unreachable!("Enum property {field_name}"), - } -} - -fn get_path_property(token: &Token, field_name: &str) -> FieldValue { - let path_token = token.as_path().expect("token was not a Path"); - match field_name { - "path" => path_token.into(), - _ => unreachable!("Path property {field_name}"), - } -} - -fn get_importable_path_property(token: &Token, field_name: &str) -> FieldValue { - let path_token = token - .as_importable_path() - .expect("token was not an ImportablePath"); - match field_name { - "path" => path_token - .iter() - .map(|x| x.to_string()) - .collect::>() - .into(), - "visibility_limit" => "public".into(), - _ => unreachable!("ImportablePath property {field_name}"), - } -} - -fn get_function_like_property(token: &Token, field_name: &str) -> FieldValue { - let maybe_function = token.as_function(); - let maybe_method = token.as_method(); - - let (header, _decl) = maybe_function - .map(|func| (&func.header, &func.decl)) - .unwrap_or_else(|| { - let method = maybe_method.unwrap_or_else(|| { - unreachable!("token was neither a function nor a method: {token:?}") - }); - (&method.header, &method.decl) - }); - - match field_name { - "const" => header.const_.into(), - "async" => header.async_.into(), - "unsafe" => header.unsafe_.into(), - _ => unreachable!("FunctionLike property {field_name}"), - } -} - -fn get_impl_property(token: &Token, field_name: &str) -> FieldValue { - let impl_token = token.as_impl().expect("token was not an Impl"); - match field_name { - "unsafe" => impl_token.is_unsafe.into(), - "negative" => impl_token.negative.into(), - "synthetic" => impl_token.synthetic.into(), - _ => unreachable!("Impl property {field_name}"), - } -} - -fn get_attribute_property(token: &Token, field_name: &str) -> FieldValue { - let attribute_token = token.as_attribute().expect("token was not an Attribute"); - match field_name { - "value" => attribute_token.into(), - _ => unreachable!("Attribute property {field_name}"), - } -} - -fn get_raw_type_property(token: &Token, field_name: &str) -> FieldValue { - let type_token = token.as_raw_type().expect("token was not a RawType"); - match field_name { - "name" => match type_token { - rustdoc_types::Type::ResolvedPath(path) => (&path.name).into(), - rustdoc_types::Type::Primitive(name) => name.into(), - _ => unreachable!("unexpected RawType token content: {type_token:?}"), - }, - _ => unreachable!("RawType property {field_name}"), - } -} - -fn get_implemented_trait_property(token: &Token, field_name: &str) -> FieldValue { - let (path, _) = token - .as_implemented_trait() - .expect("token was not a ImplementedTrait"); - match field_name { - "name" => (&path.name).into(), - _ => unreachable!("ImplementedTrait property {field_name}"), - } -} - -fn property_mapper<'a>( - ctx: DataContext>, - field_name: &str, - property_getter: fn(&Token<'a>, &str) -> FieldValue, -) -> (DataContext>, FieldValue) { - let value = match &ctx.current_token { - Some(token) => property_getter(token, field_name), - None => FieldValue::Null, - }; - (ctx, value) -} - -impl<'a> Adapter<'a> for RustdocAdapter<'a> { - type DataToken = Token<'a>; - - fn get_starting_tokens( - &mut self, - edge: Arc, - _parameters: Option>, - _query_hint: InterpretedQuery, - _vertex_hint: Vid, - ) -> Box + 'a> { - match edge.as_ref() { - "Crate" => Box::new(std::iter::once(Token::new_crate( - Origin::CurrentCrate, - self.current_crate, - ))), - "CrateDiff" => { - let previous_crate = self.previous_crate.expect("no previous crate provided"); - Box::new(std::iter::once(Token { - origin: Origin::CurrentCrate, - kind: TokenKind::CrateDiff((self.current_crate, previous_crate)), - })) - } - _ => unreachable!("{edge}"), - } - } - - fn project_property( - &mut self, - data_contexts: Box> + 'a>, - current_type_name: Arc, - field_name: Arc, - _query_hint: InterpretedQuery, - _vertex_hint: Vid, - ) -> Box, FieldValue)> + 'a> { - if field_name.as_ref() == "__typename" { - Box::new(data_contexts.map(|ctx| match &ctx.current_token { - Some(token) => { - let value = token.typename().into(); - (ctx, value) - } - None => (ctx, FieldValue::Null), - })) - } else { - match current_type_name.as_ref() { - "Crate" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_crate_property) - })) - } - "Item" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_item_property) - })) - } - "ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant" | "PlainVariant" - | "TupleVariant" | "StructVariant" | "Trait" | "Function" | "Method" | "Impl" - if matches!( - field_name.as_ref(), - "id" | "crate_id" | "name" | "docs" | "attrs" | "visibility_limit" - ) => - { - // properties inherited from Item, accesssed on Item subtypes - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_item_property) - })) - } - "Struct" => Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_struct_property) - })), - "Enum" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_enum_property) - })) - } - "Span" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_span_property) - })) - } - "Path" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_path_property) - })) - } - "ImportablePath" => Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_importable_path_property) - })), - "FunctionLike" | "Function" | "Method" - if matches!(field_name.as_ref(), "const" | "unsafe" | "async") => - { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_function_like_property) - })) - } - "Impl" => { - Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_impl_property) - })) - } - "Attribute" => Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_attribute_property) - })), - "ImplementedTrait" => Box::new(data_contexts.map(move |ctx| { - property_mapper(ctx, field_name.as_ref(), get_implemented_trait_property) - })), - "RawType" | "ResolvedPathType" | "PrimitiveType" - if matches!(field_name.as_ref(), "name") => - { - Box::new(data_contexts.map(move |ctx| { - // fields from "RawType" - property_mapper(ctx, field_name.as_ref(), get_raw_type_property) - })) - } - _ => unreachable!("project_property {current_type_name} {field_name}"), - } - } - } - - fn project_neighbors( - &mut self, - data_contexts: Box> + 'a>, - current_type_name: Arc, - edge_name: Arc, - parameters: Option>, - _query_hint: InterpretedQuery, - _vertex_hint: Vid, - _edge_hint: Eid, - ) -> Box< - dyn Iterator< - Item = ( - DataContext, - Box + 'a>, - ), - > + 'a, - > { - match current_type_name.as_ref() { - "CrateDiff" => match edge_name.as_ref() { - "current" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = match &ctx - .current_token - { - None => Box::new(std::iter::empty()), - Some(token) => { - let crate_tuple = - token.as_crate_diff().expect("token was not a CrateDiff"); - let neighbor = Token::new_crate(Origin::CurrentCrate, crate_tuple.0); - Box::new(std::iter::once(neighbor)) - } - }; - - (ctx, neighbors) - })), - "baseline" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = match &ctx - .current_token - { - None => Box::new(std::iter::empty()), - Some(token) => { - let crate_tuple = - token.as_crate_diff().expect("token was not a CrateDiff"); - let neighbor = Token::new_crate(Origin::PreviousCrate, crate_tuple.1); - Box::new(std::iter::once(neighbor)) - } - }; - - (ctx, neighbors) - })), - _ => { - unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}") - } - }, - "Crate" => { - match edge_name.as_ref() { - "item" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let crate_token = - token.as_indexed_crate().expect("token was not a Crate"); - - let iter = crate_token - .inner - .index - .values() - .filter(|item| { - // Filter out item types that are not currently supported. - matches!( - item.inner, - rustdoc_types::ItemEnum::Struct(..) - | rustdoc_types::ItemEnum::StructField(..) - | rustdoc_types::ItemEnum::Enum(..) - | rustdoc_types::ItemEnum::Variant(..) - | rustdoc_types::ItemEnum::Function(..) - | rustdoc_types::ItemEnum::Method(..) - | rustdoc_types::ItemEnum::Impl(..) - | rustdoc_types::ItemEnum::Trait(..) - ) - }) - .map(move |value| origin.make_item_token(value)); - Box::new(iter) - } - }; - - (ctx, neighbors) - })), - _ => unreachable!( - "project_neighbors {current_type_name} {edge_name} {parameters:?}" - ), - } - } - "Importable" | "ImplOwner" | "Struct" | "Enum" | "Trait" | "Function" - if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") => - { - match edge_name.as_ref() { - "canonical_path" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item = token.as_item().expect("token was not an Item"); - let item_id = &item.id; - - if let Some(path) = match origin { - Origin::CurrentCrate => current_crate - .inner - .paths - .get(item_id) - .map(|x| &x.path), - Origin::PreviousCrate => previous_crate - .expect("no baseline provided") - .inner - .paths - .get(item_id) - .map(|x| &x.path), - } { - Box::new(std::iter::once(origin.make_path_token(path))) - } else { - Box::new(std::iter::empty()) - } - } - }; - - (ctx, neighbors) - })) - } - "importable_path" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item = token.as_item().expect("token was not an Item"); - let item_id = &item.id; - - let parent_crate = match origin { - Origin::CurrentCrate => current_crate, - Origin::PreviousCrate => { - previous_crate.expect("no baseline provided") - } - }; - - Box::new( - parent_crate - .publicly_importable_names(item_id) - .into_iter() - .map(move |x| origin.make_importable_path_token(x)), - ) - } - }; - - (ctx, neighbors) - })) - } - _ => unreachable!( - "project_neighbors {current_type_name} {edge_name} {parameters:?}" - ), - } - } - "Item" | "ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant" - | "PlainVariant" | "TupleVariant" | "StructVariant" | "Trait" | "Function" - | "Method" | "Impl" - if matches!(edge_name.as_ref(), "span" | "attribute") => - { - match edge_name.as_ref() { - "span" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item = token.as_item().expect("token was not an Item"); - if let Some(span) = &item.span { - Box::new(std::iter::once(origin.make_span_token(span))) - } else { - Box::new(std::iter::empty()) - } - } - }; - - (ctx, neighbors) - })), - "attribute" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item = token.as_item().expect("token was not an Item"); - Box::new( - item.attrs - .iter() - .map(move |attr| origin.make_attribute_token(attr)), - ) - } - }; - - (ctx, neighbors) - })), - _ => unreachable!( - "project_neighbors {current_type_name} {edge_name} {parameters:?}" - ), - } - } - "ImplOwner" | "Struct" | "Enum" - if matches!(edge_name.as_ref(), "impl" | "inherent_impl") => - { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - let inherent_impls_only = edge_name.as_ref() == "inherent_impl"; - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item_index = match origin { - Origin::CurrentCrate => ¤t_crate.inner.index, - Origin::PreviousCrate => { - &previous_crate - .expect("no previous crate provided") - .inner - .index - } - }; - - // Get the IDs of all the impl blocks. - // Relies on the fact that only structs and enums can have impls, - // so we know that the token must represent either a struct or an enum. - let impl_ids = token - .as_struct_item() - .map(|(_, s)| &s.impls) - .or_else(|| token.as_enum().map(|e| &e.impls)) - .expect("token was neither a struct nor an enum"); - - Box::new(impl_ids.iter().filter_map(move |item_id| { - let next_item = item_index.get(item_id); - next_item.and_then(|next_item| match &next_item.inner { - rustdoc_types::ItemEnum::Impl(imp) => { - if !inherent_impls_only || imp.trait_.is_none() { - Some(origin.make_item_token(next_item)) - } else { - None - } - } - _ => None, - }) - })) - } - }; - - (ctx, neighbors) - })) - } - "Struct" => match edge_name.as_ref() { - "field" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = match &ctx - .current_token - { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let (_, struct_item) = - token.as_struct_item().expect("token was not a Struct"); - - let item_index = match origin { - Origin::CurrentCrate => ¤t_crate.inner.index, - Origin::PreviousCrate => { - &previous_crate - .expect("no previous crate provided") - .inner - .index - } - }; - - let field_ids_iter: Box> = - match &struct_item.kind { - rustdoc_types::StructKind::Unit => { - Box::new(std::iter::empty()) - } - rustdoc_types::StructKind::Tuple(field_ids) => { - Box::new(field_ids.iter().filter_map(|x| x.as_ref())) - } - rustdoc_types::StructKind::Plain { fields, .. } => { - Box::new(fields.iter()) - } - }; - - Box::new(field_ids_iter.map(move |field_id| { - origin.make_item_token( - item_index.get(field_id).expect("missing item"), - ) - })) - } - }; - - (ctx, neighbors) - })) - } - _ => { - unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}") - } - }, - "Enum" => match edge_name.as_ref() { - "variant" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let enum_item = token.as_enum().expect("token was not a Enum"); - - let item_index = match origin { - Origin::CurrentCrate => ¤t_crate.inner.index, - Origin::PreviousCrate => { - &previous_crate - .expect("no previous crate provided") - .inner - .index - } - }; - Box::new(enum_item.variants.iter().map(move |field_id| { - origin.make_item_token( - item_index.get(field_id).expect("missing item"), - ) - })) - } - }; - - (ctx, neighbors) - })) - } - _ => { - unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}") - } - }, - "StructField" => match edge_name.as_ref() { - "raw_type" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let (_, field_type) = token - .as_struct_field_item() - .expect("not a StructField token"); - Box::new(std::iter::once(origin.make_raw_type_token(field_type))) - } - }; - - (ctx, neighbors) - })), - _ => { - unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}") - } - }, - "Impl" => { - match edge_name.as_ref() { - "method" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = match &ctx - .current_token - { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item_index = match origin { - Origin::CurrentCrate => ¤t_crate.inner.index, - Origin::PreviousCrate => { - &previous_crate.expect("no previous crate provided").inner.index - } - }; - - let impl_token = token.as_impl().expect("not an Impl token"); - let provided_methods: Box> = if impl_token.provided_trait_methods.is_empty() { - Box::new(std::iter::empty()) - } else { - let method_names: BTreeSet<&str> = impl_token.provided_trait_methods.iter().map(|x| x.as_str()).collect(); - - let trait_path = impl_token.trait_.as_ref().expect("no trait but provided_trait_methods was non-empty"); - let trait_item = item_index.get(&trait_path.id); - - if let Some(trait_item) = trait_item { - if let ItemEnum::Trait(trait_item) = &trait_item.inner { - Box::new(trait_item.items.iter().filter(move |item_id| { - let next_item = &item_index.get(item_id); - if let Some(name) = next_item.and_then(|x| x.name.as_deref()) { - method_names.contains(name) - } else { - false - } - })) - } else { - unreachable!("found a non-trait type {trait_item:?}"); - } - } else { - Box::new(std::iter::empty()) - } - }; - Box::new(provided_methods.chain(impl_token.items.iter()).filter_map(move |item_id| { - let next_item = &item_index.get(item_id); - if let Some(next_item) = next_item { - match &next_item.inner { - rustdoc_types::ItemEnum::Method(..) => { - Some(origin.make_item_token(next_item)) - } - _ => None, - } - } else { - None - } - })) - } - }; - - (ctx, neighbors) - })) - } - "implemented_trait" => { - let current_crate = self.current_crate; - let previous_crate = self.previous_crate; - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item_index = match origin { - Origin::CurrentCrate => ¤t_crate.inner.index, - Origin::PreviousCrate => { - &previous_crate - .expect("no previous crate provided") - .inner - .index - } - }; - - let impl_token = - token.as_impl().expect("not an Impl token"); - - if let Some(path) = &impl_token.trait_ { - if let Some(item) = item_index.get(&path.id) { - Box::new(std::iter::once( - origin.make_implemented_trait_token(path, item), - )) - } else { - Box::new(std::iter::empty()) - } - } else { - Box::new(std::iter::empty()) - } - } - }; - - (ctx, neighbors) - })) - } - _ => { - unreachable!( - "project_neighbors {current_type_name} {edge_name} {parameters:?}" - ) - } - } - } - "ImplementedTrait" => match edge_name.as_ref() { - "trait" => Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - - let (_, trait_item) = token - .as_implemented_trait() - .expect("token was not an ImplementedTrait"); - Box::new(std::iter::once(origin.make_item_token(trait_item))) - } - }; - - (ctx, neighbors) - })), - _ => { - unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}") - } - }, - _ => unreachable!("project_neighbors {current_type_name} {edge_name} {parameters:?}"), - } - } - - fn can_coerce_to_type( - &mut self, - data_contexts: Box> + 'a>, - current_type_name: Arc, - coerce_to_type_name: Arc, - _query_hint: InterpretedQuery, - _vertex_hint: Vid, - ) -> Box, bool)> + 'a> { - match current_type_name.as_ref() { - "Item" | "Variant" | "FunctionLike" | "Importable" | "ImplOwner" | "RawType" - | "ResolvedPathType" => { - Box::new(data_contexts.map(move |ctx| { - let can_coerce = match &ctx.current_token { - None => false, - Some(token) => { - let actual_type_name = token.typename(); - - match coerce_to_type_name.as_ref() { - "Variant" => matches!( - actual_type_name, - "PlainVariant" | "TupleVariant" | "StructVariant" - ), - "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum"), - "ResolvedPathType" => matches!( - actual_type_name, - "ResolvedPathType" | "ImplementedTrait" - ), - _ => { - // The remaining types are final (don't have any subtypes) - // so we can just compare the actual type name to - // the type we are attempting to coerce to. - actual_type_name == coerce_to_type_name.as_ref() - } - } - } - }; - - (ctx, can_coerce) - })) - } - _ => unreachable!("can_coerce_to_type {current_type_name} {coerce_to_type_name}"), - } - } -} - -#[cfg(test)] -mod tests { - use std::path::Path; - use std::{cell::RefCell, collections::BTreeMap, rc::Rc, sync::Arc}; - - use anyhow::Context; - use trustfall_core::{frontend::parse, interpreter::execution::interpret_ir, ir::FieldValue}; - - use crate::{indexed_crate::IndexedCrate, query::SemverQuery, util::load_rustdoc_from_file}; - - use super::RustdocAdapter; - - #[test] - fn rustdoc_json_format_version() { - let current_crate = load_rustdoc_from_file(Path::new("./localdata/test_data/baseline.json")) - .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") - .expect("failed to load baseline rustdoc"); - - assert_eq!(current_crate.format_version, rustdoc_types::FORMAT_VERSION); - } - - #[test] - fn pub_use_handling() { - let current_crate = load_rustdoc_from_file(Path::new("./localdata/test_data/baseline.json")) - .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") - .expect("failed to load baseline rustdoc"); - - let current = IndexedCrate::new(¤t_crate); - - let query = r#" - { - Crate { - item { - ... on Struct { - name @filter(op: "=", value: ["$struct"]) - - canonical_path { - canonical_path: path @output - } - - importable_path @fold { - path @output - } - } - } - } - }"#; - let mut arguments = BTreeMap::new(); - arguments.insert("struct", "CheckPubUseHandling"); - - let schema = RustdocAdapter::schema(); - let adapter = Rc::new(RefCell::new(RustdocAdapter::new(¤t, None))); - - let parsed_query = parse(&schema, query).unwrap(); - let args = Arc::new( - arguments - .iter() - .map(|(k, v)| (Arc::from(k.to_string()), (*v).into())) - .collect(), - ); - let results_iter = interpret_ir(adapter.clone(), parsed_query, args).unwrap(); - - let actual_results: Vec> = results_iter - .map(|res| res.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) - .collect(); - - let expected_result: FieldValue = vec![ - "semver_tests", - "import_handling", - "inner", - "CheckPubUseHandling", - ] - .into(); - assert_eq!(1, actual_results.len(), "{actual_results:?}"); - assert_eq!( - expected_result, actual_results[0]["canonical_path"], - "{actual_results:?}" - ); - - let mut actual_paths = actual_results[0]["path"] - .as_vec(|val| val.as_vec(FieldValue::as_str)) - .expect("not a Vec>"); - actual_paths.sort_unstable(); - - let expected_paths = vec![ - vec!["semver_tests", "CheckPubUseHandling"], - vec!["semver_tests", "import_handling", "CheckPubUseHandling"], - vec![ - "semver_tests", - "import_handling", - "inner", - "CheckPubUseHandling", - ], - ]; - assert_eq!(expected_paths, actual_paths); - } - - fn check_query_execution(query_name: &str) { - // Ensure the rustdocs JSON outputs have been regenerated. - let baseline_crate = load_rustdoc_from_file(Path::new("./localdata/test_data/baseline.json")) - .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") - .expect("failed to load baseline rustdoc"); - let current_crate = - load_rustdoc_from_file(Path::new(&format!("./localdata/test_data/{}.json", query_name))) - .with_context(|| format!("Could not load localdata/test_data/{}.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?", query_name)) - .expect("failed to load rustdoc under test"); - - let baseline = IndexedCrate::new(&baseline_crate); - let current = IndexedCrate::new(¤t_crate); - - let query_text = - std::fs::read_to_string(&format!("./src/queries/{}.ron", query_name)).unwrap(); - let semver_query: SemverQuery = ron::from_str(&query_text).unwrap(); - - let expected_result_text = - std::fs::read_to_string(&format!("./src/test_data/{}.output.ron", query_name)) - .with_context(|| format!("Could not load src/test_data/{}.output.ron expected-outputs file, did you forget to add it?", query_name)) - .expect("failed to load expected outputs"); - let mut expected_results: Vec> = - ron::from_str(&expected_result_text) - .expect("could not parse expected outputs as ron format"); - - let schema = RustdocAdapter::schema(); - let adapter = Rc::new(RefCell::new(RustdocAdapter::new(¤t, Some(&baseline)))); - - let parsed_query = parse(&schema, &semver_query.query).unwrap(); - let args = Arc::new( - semver_query - .arguments - .iter() - .map(|(k, v)| (Arc::from(k.clone()), v.clone().into())) - .collect(), - ); - let results_iter = interpret_ir(adapter.clone(), parsed_query, args).unwrap(); - - let mut actual_results: Vec> = results_iter - .map(|res| res.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) - .collect(); - - // Reorder both vectors of results into a deterministic order that will compensate for - // nondeterminism in how the results are ordered. - let key_func = |elem: &BTreeMap| { - ( - elem["span_filename"].as_str().unwrap().to_owned(), - elem["span_begin_line"].as_usize().unwrap(), - ) - }; - expected_results.sort_unstable_by_key(key_func); - actual_results.sort_unstable_by_key(key_func); - - assert_eq!(expected_results, actual_results); - } - - macro_rules! query_execution_tests { - ($($name:ident,)*) => { - $( - #[test] - fn $name() { - check_query_execution(stringify!($name)) - } - )* - } - } - - query_execution_tests!( - auto_trait_impl_removed, - derive_trait_impl_removed, - enum_missing, - enum_repr_c_removed, - enum_repr_int_changed, - enum_repr_int_removed, - enum_variant_added, - enum_variant_missing, - function_missing, - inherent_method_missing, - sized_impl_removed, - struct_marked_non_exhaustive, - struct_missing, - struct_pub_field_missing, - struct_repr_c_removed, - struct_repr_transparent_removed, - unit_struct_changed_kind, - variant_marked_non_exhaustive, - ); -} diff --git a/src/check_release.rs b/src/check_release.rs index b53f2270..3a4a5398 100644 --- a/src/check_release.rs +++ b/src/check_release.rs @@ -1,23 +1,13 @@ -use std::{ - cell::RefCell, collections::BTreeMap, env, io::Write, iter::Peekable, rc::Rc, sync::Arc, - time::Duration, -}; +use std::{collections::BTreeMap, env, io::Write, iter::Peekable, sync::Arc, time::Duration}; use anyhow::Context; use clap::crate_version; -use rustdoc_types::Crate; use termcolor::Color; use termcolor_output::{colored, colored_ln}; -use trustfall_core::{ - frontend::parse, - interpreter::execution::interpret_ir, - ir::{FieldValue, TransparentValue}, - schema::Schema, -}; +use trustfall_core::ir::{FieldValue, TransparentValue}; +use trustfall_rustdoc::{VersionedCrate, VersionedIndexedCrate, VersionedRustdocAdapter}; use crate::{ - adapter::RustdocAdapter, - indexed_crate::IndexedCrate, query::{ActualSemverUpdate, RequiredSemverUpdate, SemverQuery}, GlobalConfig, }; @@ -87,35 +77,14 @@ fn classify_semver_version_change( } } -fn make_result_iter<'a>( - schema: &Schema, - adapter: Rc>>, - semver_query: &SemverQuery, -) -> anyhow::Result + 'a>>> { - let parsed_query = parse(schema, &semver_query.query) - .expect("not a valid query, should have been caught in tests"); - let args = Arc::new( - semver_query - .arguments - .iter() - .map(|(k, v)| (Arc::from(k.clone()), v.clone().into())) - .collect(), - ); - let results_iter = interpret_ir(adapter.clone(), parsed_query, args) - .with_context(|| "Query execution error.")? - .peekable(); - - Ok(results_iter) -} - pub(super) fn run_check_release( config: &mut GlobalConfig, crate_name: &str, - current_crate: Crate, - baseline_crate: Crate, + current_crate: VersionedCrate, + baseline_crate: VersionedCrate, ) -> anyhow::Result { - let current_version = current_crate.crate_version.as_deref(); - let baseline_version = baseline_crate.crate_version.as_deref(); + let current_version = current_crate.crate_version(); + let baseline_version = baseline_crate.crate_version(); let version_change = classify_semver_version_change(current_version, baseline_version) .unwrap_or_else(|| { @@ -135,10 +104,9 @@ pub(super) fn run_check_release( let queries = SemverQuery::all_queries(); - let schema = RustdocAdapter::schema(); - let current = IndexedCrate::new(¤t_crate); - let previous = IndexedCrate::new(&baseline_crate); - let adapter = Rc::new(RefCell::new(RustdocAdapter::new(¤t, Some(&previous)))); + let current = VersionedIndexedCrate::new(¤t_crate); + let previous = VersionedIndexedCrate::new(&baseline_crate); + let adapter = VersionedRustdocAdapter::new(¤t, Some(&previous))?; let mut queries_with_errors: Vec = vec![]; let queries_to_run: Vec<_> = queries @@ -196,7 +164,9 @@ pub(super) fn run_check_release( .expect("print failed"); let start_instant = std::time::Instant::now(); - let mut results_iter = make_result_iter(&schema, adapter.clone(), semver_query)?; + let mut results_iter = adapter + .run_query(&semver_query.query, semver_query.arguments.clone())? + .peekable(); let peeked = results_iter.peek(); let end_instant = std::time::Instant::now(); let time_to_decide = end_instant - start_instant; diff --git a/src/indexed_crate.rs b/src/indexed_crate.rs deleted file mode 100644 index c381edbf..00000000 --- a/src/indexed_crate.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::collections::HashMap; - -use rustdoc_types::{Crate, Id, Item, Visibility}; - -#[derive(Debug, Clone)] -pub struct IndexedCrate<'a> { - pub(crate) inner: &'a Crate, - - // For an Id, give the list of item Ids under which it is publicly visible. - pub(crate) visibility_forest: HashMap<&'a Id, Vec<&'a Id>>, -} - -impl<'a> IndexedCrate<'a> { - pub fn new(crate_: &'a Crate) -> Self { - let visibility_forest = calculate_visibility_forest(crate_); - - Self { - inner: crate_, - visibility_forest, - } - } - - pub fn publicly_importable_names(&self, id: &'a Id) -> Vec> { - let mut result = vec![]; - - if self.inner.index.contains_key(id) { - self.collect_publicly_importable_names(id, &mut vec![], &mut result); - } - - result - } - - fn collect_publicly_importable_names( - &self, - next_id: &'a Id, - stack: &mut Vec<&'a str>, - output: &mut Vec>, - ) { - let item = &self.inner.index[next_id]; - if let Some(item_name) = item.name.as_deref() { - stack.push(item_name); - } else { - assert!( - matches!(item.inner, rustdoc_types::ItemEnum::Import(..)), - "{item:?}" - ); - } - - if next_id == &self.inner.root { - let final_name = stack.iter().rev().copied().collect(); - output.push(final_name); - } else if let Some(visible_parents) = self.visibility_forest.get(next_id) { - for parent_id in visible_parents.iter().copied() { - self.collect_publicly_importable_names(parent_id, stack, output); - } - } - - if let Some(item_name) = item.name.as_deref() { - let popped_item = stack.pop().expect("stack was unexpectedly empty"); - assert_eq!(item_name, popped_item); - } - } -} - -fn calculate_visibility_forest(crate_: &Crate) -> HashMap<&Id, Vec<&Id>> { - let mut result = Default::default(); - let root_id = &crate_.root; - if let Some(root_module) = crate_.index.get(root_id) { - if root_module.visibility == Visibility::Public { - collect_public_items(crate_, &mut result, root_module, None); - } - } - - result -} - -fn collect_public_items<'a>( - crate_: &'a Crate, - pub_items: &mut HashMap<&'a Id, Vec<&'a Id>>, - item: &'a Item, - parent_id: Option<&'a Id>, -) { - match item.visibility { - // Some impls and methods have default visibility: - // they are visible only if the type to which they belong is visible. - // However, we don't recurse into non-public items with this function, so - // reachable items with default visibility must be public. - Visibility::Public | Visibility::Default => { - let parents = pub_items.entry(&item.id).or_default(); - if let Some(parent_id) = parent_id { - parents.push(parent_id); - } - - let next_parent_id = Some(&item.id); - match &item.inner { - rustdoc_types::ItemEnum::Module(m) => { - for inner in m.items.iter().filter_map(|id| crate_.index.get(id)) { - collect_public_items(crate_, pub_items, inner, next_parent_id); - } - } - rustdoc_types::ItemEnum::Import(imp) => { - // TODO: handle glob imports (`pub use foo::bar::*`) here. - if let Some(item) = imp.id.as_ref().and_then(|id| crate_.index.get(id)) { - collect_public_items(crate_, pub_items, item, next_parent_id); - } - } - rustdoc_types::ItemEnum::Struct(struct_) => { - let field_ids_iter: Box> = match &struct_.kind { - rustdoc_types::StructKind::Unit => Box::new(std::iter::empty()), - rustdoc_types::StructKind::Tuple(field_ids) => { - Box::new(field_ids.iter().filter_map(|x| x.as_ref())) - } - rustdoc_types::StructKind::Plain { fields, .. } => Box::new(fields.iter()), - }; - - for inner in field_ids_iter - .chain(struct_.impls.iter()) - .filter_map(|id| crate_.index.get(id)) - { - collect_public_items(crate_, pub_items, inner, next_parent_id); - } - } - rustdoc_types::ItemEnum::Enum(enum_) => { - for inner in enum_ - .variants - .iter() - .chain(enum_.impls.iter()) - .filter_map(|id| crate_.index.get(id)) - { - collect_public_items(crate_, pub_items, inner, next_parent_id); - } - } - rustdoc_types::ItemEnum::Trait(trait_) => { - for inner in trait_.items.iter().filter_map(|id| crate_.index.get(id)) { - collect_public_items(crate_, pub_items, inner, next_parent_id); - } - } - rustdoc_types::ItemEnum::Impl(impl_) => { - for inner in impl_.items.iter().filter_map(|id| crate_.index.get(id)) { - collect_public_items(crate_, pub_items, inner, next_parent_id); - } - } - _ => { - // No-op, no further items within to consider. - } - } - } - Visibility::Crate | Visibility::Restricted { .. } => {} - } -} diff --git a/src/main.rs b/src/main.rs index 7fc8e861..b7a10ceb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ #![forbid(unsafe_code)] -pub mod adapter; mod baseline; mod check_release; mod config; mod dump; -pub mod indexed_crate; mod manifest; mod query; mod templating; @@ -14,10 +12,9 @@ mod util; use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; +use trustfall_rustdoc::load_rustdoc; -use crate::{check_release::run_check_release, util::load_rustdoc_from_file}; -use config::GlobalConfig; -use util::slugify; +use crate::{check_release::run_check_release, config::GlobalConfig, util::slugify}; fn main() -> anyhow::Result<()> { human_panic::setup_panic!(); @@ -175,8 +172,8 @@ fn main() -> anyhow::Result<()> { }; let mut success = true; for (crate_name, baseline_path, current_path) in rustdoc_paths { - let baseline_crate = load_rustdoc_from_file(&baseline_path)?; - let current_crate = load_rustdoc_from_file(¤t_path)?; + let baseline_crate = load_rustdoc(&baseline_path)?; + let current_crate = load_rustdoc(¤t_path)?; if !run_check_release(&mut config, &crate_name, current_crate, baseline_crate)? { success = false; diff --git a/src/query.rs b/src/query.rs index 993c99eb..f492829f 100644 --- a/src/query.rs +++ b/src/query.rs @@ -116,17 +116,177 @@ Failed to parse a query: {} #[cfg(test)] mod tests { - use trustfall_core::frontend::parse; + use std::{collections::BTreeMap, path::Path}; - use crate::adapter::RustdocAdapter; + use anyhow::Context; + use trustfall_core::{frontend::parse, ir::FieldValue}; + use trustfall_rustdoc::{load_rustdoc, VersionedIndexedCrate, VersionedRustdocAdapter}; - use super::SemverQuery; + use crate::query::SemverQuery; #[test] fn all_queries_parse_correctly() { - let schema = RustdocAdapter::schema(); + let current_crate = load_rustdoc(Path::new("./localdata/test_data/baseline.json")) + .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") + .expect("failed to load baseline rustdoc"); + let indexed_crate = VersionedIndexedCrate::new(¤t_crate); + let adapter = + VersionedRustdocAdapter::new(&indexed_crate, None).expect("failed to create adapter"); + + let schema = adapter.schema(); for semver_query in SemverQuery::all_queries().into_values() { - let _ = parse(&schema, &semver_query.query).expect("not a valid query"); + let _ = parse(schema, &semver_query.query).expect("not a valid query"); } } + + #[test] + fn pub_use_handling() { + let current_crate = load_rustdoc(Path::new("./localdata/test_data/baseline.json")) + .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") + .expect("failed to load baseline rustdoc"); + + let current = VersionedIndexedCrate::new(¤t_crate); + + let query = r#" + { + Crate { + item { + ... on Struct { + name @filter(op: "=", value: ["$struct"]) + + canonical_path { + canonical_path: path @output + } + + importable_path @fold { + path @output + } + } + } + } + }"#; + let mut arguments = BTreeMap::new(); + arguments.insert("struct", "CheckPubUseHandling"); + + let adapter = + VersionedRustdocAdapter::new(¤t, None).expect("could not create adapter"); + + let results_iter = adapter + .run_query(query, arguments) + .expect("failed to run query"); + let actual_results: Vec> = results_iter + .map(|res| res.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) + .collect(); + + let expected_result: FieldValue = vec![ + "semver_tests", + "import_handling", + "inner", + "CheckPubUseHandling", + ] + .into(); + assert_eq!(1, actual_results.len(), "{actual_results:?}"); + assert_eq!( + expected_result, actual_results[0]["canonical_path"], + "{actual_results:?}" + ); + + let mut actual_paths = actual_results[0]["path"] + .as_vec(|val| val.as_vec(FieldValue::as_str)) + .expect("not a Vec>"); + actual_paths.sort_unstable(); + + let expected_paths = vec![ + vec!["semver_tests", "CheckPubUseHandling"], + vec!["semver_tests", "import_handling", "CheckPubUseHandling"], + vec![ + "semver_tests", + "import_handling", + "inner", + "CheckPubUseHandling", + ], + ]; + assert_eq!(expected_paths, actual_paths); + } + + fn check_query_execution(query_name: &str) { + // Ensure the rustdocs JSON outputs have been regenerated. + let baseline_crate = load_rustdoc(Path::new("./localdata/test_data/baseline.json")) + .with_context(|| "Could not load localdata/test_data/baseline.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?") + .expect("failed to load baseline rustdoc"); + let current_crate = + load_rustdoc(Path::new(&format!("./localdata/test_data/{}.json", query_name))) + .with_context(|| format!("Could not load localdata/test_data/{}.json file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?", query_name)) + .expect("failed to load rustdoc under test"); + + let baseline = VersionedIndexedCrate::new(&baseline_crate); + let current = VersionedIndexedCrate::new(¤t_crate); + + let query_text = + std::fs::read_to_string(&format!("./src/queries/{}.ron", query_name)).unwrap(); + let semver_query: SemverQuery = ron::from_str(&query_text).unwrap(); + + let expected_result_text = + std::fs::read_to_string(&format!("./src/test_data/{}.output.ron", query_name)) + .with_context(|| format!("Could not load src/test_data/{}.output.ron expected-outputs file, did you forget to add it?", query_name)) + .expect("failed to load expected outputs"); + let mut expected_results: Vec> = + ron::from_str(&expected_result_text) + .expect("could not parse expected outputs as ron format"); + + let adapter = VersionedRustdocAdapter::new(¤t, Some(&baseline)) + .expect("could not create adapter"); + let results_iter = adapter + .run_query(&semver_query.query, semver_query.arguments.clone()) + .unwrap(); + + let mut actual_results: Vec> = results_iter + .map(|res| res.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) + .collect(); + + // Reorder both vectors of results into a deterministic order that will compensate for + // nondeterminism in how the results are ordered. + let key_func = |elem: &BTreeMap| { + ( + elem["span_filename"].as_str().unwrap().to_owned(), + elem["span_begin_line"].as_usize().unwrap(), + ) + }; + expected_results.sort_unstable_by_key(key_func); + actual_results.sort_unstable_by_key(key_func); + + assert_eq!(expected_results, actual_results); + } + + macro_rules! query_execution_tests { + ($($name:ident,)*) => { + $( + #[test] + fn $name() { + check_query_execution(stringify!($name)) + } + )* + } + } + + query_execution_tests!( + auto_trait_impl_removed, + derive_trait_impl_removed, + enum_missing, + enum_repr_c_removed, + enum_repr_int_changed, + enum_repr_int_removed, + enum_variant_added, + enum_variant_missing, + function_missing, + inherent_method_missing, + sized_impl_removed, + struct_marked_non_exhaustive, + struct_missing, + struct_pub_field_missing, + struct_repr_c_removed, + struct_repr_transparent_removed, + unit_struct_changed_kind, + variant_marked_non_exhaustive, + ); } diff --git a/src/rustdoc_schema.graphql b/src/rustdoc_schema.graphql deleted file mode 100644 index 8974ad0a..00000000 --- a/src/rustdoc_schema.graphql +++ /dev/null @@ -1,581 +0,0 @@ -schema { - query: RootSchemaQuery -} -directive @filter(op: String!, value: [String!]) on FIELD | INLINE_FRAGMENT -directive @tag(name: String) on FIELD -directive @output(name: String) on FIELD -directive @optional on FIELD -directive @recurse(depth: Int!) on FIELD -directive @fold on FIELD - -type RootSchemaQuery { - Crate: Crate! - CrateDiff: CrateDiff! -} - -type CrateDiff { - current: Crate! - baseline: Crate -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Crate.html -""" -type Crate { - root: String! - crate_version: String - includes_private: Boolean! - format_version: Int! - - item: [Item!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -""" -interface Item { - id: String! - crate_id: Int! - name: String - docs: String - - """ - A list of all the attributes applied to this item. - - The attributes are also available through the `attribute` edge, - which makes certain operations easier. - """ - attrs: [String!]! - - # stringified version of the visibility struct field - visibility_limit: String! - - attribute: [Attribute!] - span: Span -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Struct.html -""" -type Struct implements Item & Importable & ImplOwner { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # own properties - struct_type: String! - fields_stripped: Boolean! - - # edges from Item - span: Span - attribute: [Attribute!] - - # edges from Importable - importable_path: [ImportablePath!] - canonical_path: Path - - # edges from ImplOwner - """ - Any impl for this type. - - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] - - """ - Only inherent impls: implementations of the type itself (`impl Foo`). - - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. - - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] - - # own edges - field: [StructField!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Type.html -""" -type StructField implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] - - # own edges - raw_type: RawType -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Enum.html -""" -type Enum implements Item & Importable & ImplOwner { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # own properties - variants_stripped: Boolean! - - # edges from Item - span: Span - attribute: [Attribute!] - - # edges from Importable - importable_path: [ImportablePath!] - canonical_path: Path - - # edges from ImplOwner - """ - Any impl for this type. - - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] - - """ - Only inherent impls: implementations of the type itself (`impl Foo`). - - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. - - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] - - # own edges - variant: [Variant!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html -""" -interface Variant implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html -""" -type PlainVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html -""" -type TupleVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html -""" -type StructVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Span.html -""" -type Span { - filename: String! - begin_line: Int! - begin_column: Int! - end_line: Int! - end_column: Int! -} - -""" -An item that can be imported, through one or more paths. -""" -interface Importable { - importable_path: [ImportablePath!] - canonical_path: Path -} - -""" -An item that can have impl blocks, like a struct or enum. -""" -interface ImplOwner implements Item & Importable { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - attribute: [Attribute!] - - # edges from Importable - importable_path: [ImportablePath!] - canonical_path: Path - - # own edges - """ - Any impl for this type. - - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] - - """ - Only inherent impls: implementations of the type itself (`impl Foo`). - - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. - - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/latest/rustdoc_types/struct.Impl.html -""" -type Impl implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - - # stringified version of the visibility struct field - visibility_limit: String! - - # own properties - unsafe: Boolean! - negative: Boolean! - synthetic: Boolean! - - # edges from Item - span: Span - attribute: [Attribute!] - - # own edges - - """ - The trait being implemented. Inherent impls don't have a trait. - - TODO: implement me - """ - implemented_trait: ImplementedTrait - - # """ - # The generic type across which the blanket trait implementation is made. - - # TODO: implement me - # """ - # blanket: GenericType - - """ - Methods defined in this impl. - """ - method: [Method!] -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/latest/rustdoc_types/struct.Trait.html -""" -type Trait implements Item & Importable { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # own properties - variants_stripped: Boolean! - - # edges from Item - span: Span - attribute: [Attribute!] - - # edges from Importable - importable_path: [ImportablePath!] - canonical_path: Path -} - -""" -A possible way that an item could be imported. -""" -type ImportablePath { - """ - The visibility restriction on this importable path. - - For example: "public" - """ - visibility_limit: String! - - """ - The path from which the item can be imported. - - For example: ["foo", "bar", "Baz"] for a type importable as foo::bar::Baz - """ - path: [String!]! -} - -""" -The fully-qualified path of an item including the full sequence of modules in which it is found. - -For example, consider a struct `Quux` in `foo/bar/mod.rs`. Its canonical path is `"foo::bar::Quux"`, -even if `foo/mod.rs` might have a line like `pub use bar::Quux;`. The re-export is visible through -the struct's ImportablePath neighbors. -""" -type Path { - path: [String!]! -} - -""" -A function-like entity, like a function, function pointer, or method. - -Combines: -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Header.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.FnDecl.html -""" -interface FunctionLike { - const: Boolean! - unsafe: Boolean! - async: Boolean! -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Function.html -""" -type Function implements Item & FunctionLike & Importable { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # properties from FunctionLike - const: Boolean! - unsafe: Boolean! - async: Boolean! - - # edges from Item - span: Span - attribute: [Attribute!] - - # edges from Importable - importable_path: [ImportablePath!] - canonical_path: Path -} - -""" -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html -https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Method.html -""" -type Method implements Item & FunctionLike { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # properties from FunctionLike - const: Boolean! - unsafe: Boolean! - async: Boolean! - - # edge from Item - span: Span - attribute: [Attribute!] -} - -""" -A specific attribute applied to an Item. -""" -type Attribute { - """ - The textual representation of the attribute. - - For example: "#[repr(C)]" - """ - value: String! -} - -""" -A type represented in the "raw" rustdoc JSON representation. - -Copiously detailed, but not the easiest to use due to its complexity. - -This interface is a temporary, perma-unstable type intended to be used -only until the rustdoc JSON format is stabilized and until subsequently -we are able to design a better, more permanent representation for -Rust types in this schema. - -https://docs.rs/rustdoc-types/latest/rustdoc_types/enum.Type.html -""" -interface RawType { - name: String! -} - -""" -Represents a struct, enum, or trait. - -https://docs.rs/rustdoc-types/latest/rustdoc_types/enum.Type.html#variant.ResolvedPath -""" -interface ResolvedPathType implements RawType { - """ - The fully-qualified canonical name of the type. - - For example: "core::marker::PhantomData" or "std::marker::PhantomData" - """ - name: String! -} - -""" -The trait that is being implemented in an impl block. - -In `impl Foo for Bar`, this is the `Foo` part. -""" -type ImplementedTrait { - name: String! - - # own edges - """ - In `impl Foo for Bar`, this refers to `trait Foo`. - """ - trait: Trait! -} - -""" -Represents a primitive type: -fixed-size numeric types (plus int/usize/float), char, arrays, slices, and tuples. - -https://docs.rs/rustdoc-types/latest/rustdoc_types/enum.Type.html#variant.Primitive -""" -type PrimitiveType implements RawType { - """ - The name of the primitive type. - - For example: "usize" - """ - name: String! -} - -""" -Any other type that isn't currently captured by another kind of RawType. - -This type is a hack, so I can get useful queries running before implementing -the entire spectrum of possible types. - -No query should write "... on OtherType" because there's nothing you can do with it. -It will eventually be removed without a major version bump. -""" -type OtherType implements RawType { - """ - As best we can define the name of this type. - """ - name: String! -} diff --git a/src/util.rs b/src/util.rs index 90d49973..66014804 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,85 +1,5 @@ -use std::path::Path; -use std::{fs::File, io::Read}; - -use anyhow::{bail, Context}; -use rustdoc_types::Crate; -use serde::Deserialize; - pub(crate) const SCOPE: &str = "semver-checks"; -#[derive(Deserialize)] -struct RustdocFormatVersion { - format_version: u32, -} - -pub(crate) fn load_rustdoc_from_file(path: &Path) -> anyhow::Result { - // Parsing JSON after fully reading a file into memory is much faster than - // parsing directly from a file, even if buffered: - // https://github.com/serde-rs/json/issues/160 - let mut s = String::new(); - File::open(path) - .with_context(|| format!("failed to open rustdoc JSON file {}", path.display()))? - .read_to_string(&mut s) - .with_context(|| format!("failed to read rustdoc JSON file {}", path.display()))?; - - match serde_json::from_str(&s) { - Ok(value) => Ok(value), - Err(e) => { - // Attempt to figure out the more precise reason the deserialization failed. - // Several possible options and their resolutions: - // (1) The file isn't actually a rustdoc JSON file. The user should supply a valid file. - // (2) The rustdoc JSON file has a version number that is too old, and isn't supported. - // The user should upgrade to a newer nightly Rust version and regenerate the file. - // (3) The rustdoc JSON file has a version number that is too new, and isn't supported. - // The user should attempt to upgrade to a newer cargo-semver-checks version - // if one is already available, or open a GitHub issue otherwise. - - // The error on this line is case (1). - let version = serde_json::from_str::(&s).with_context(|| { - format!("unrecognized rustdoc format for file {}", path.display(),) - })?; - - match version.format_version.cmp(&rustdoc_types::FORMAT_VERSION) { - std::cmp::Ordering::Less => { - // The error here is case (2). - bail!( - "\ -rustdoc output format is too old (v{1}, need v{0}) for file {2} - -note: using a newer Rust nightly version should help", - rustdoc_types::FORMAT_VERSION, - version.format_version, - path.display(), - ) - } - std::cmp::Ordering::Greater => { - // The error here is case (3). - bail!( - "\ -rustdoc output format is too new (v{1}, need v{0}) when parsing {2} - -note: a newer version of cargo-semver-checks is likely available", - rustdoc_types::FORMAT_VERSION, - version.format_version, - path.display(), - ) - } - std::cmp::Ordering::Equal => Err(e).with_context(|| { - format!( - "\ -unexpected parse error for v{} rustdoc for file {} - -note: this is a bug, please report it on the tool's GitHub together with \ -the output of `cargo-semver-checks --bugreport`", - rustdoc_types::FORMAT_VERSION, - path.display(), - ) - }), - } - } - } -} - pub(crate) fn slugify(value: &str) -> String { value .chars()