From eaf51b32df901bb2087c3677977af4944e4afe74 Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Fri, 8 Jul 2022 14:53:34 +0200 Subject: [PATCH 1/4] Rustdoc backend Bump version Clean up code Initial comparison works Add deprecation Handle enum variants Add tests Cargo fix Clean up code Remove old git module Clean up code Better error handling Handle methods (for later) Add Display for rustdoc objects Fix rustdoc call for impls Add method metadata type Update types.rs It just works Remove redundant method fix+fmt Remove leftover glue crate code Update glue.rs clippy remove redundant lines from output fmt Build lib Optional type resolution Try add nightly to workflow angry clippy Update types.rs Handle removal of private fields (fixes #43) Update rustdoc_types to 0.11.0 Fix typo in name Remove leftover argument Better arg help Delete leftover files Remove leftover commented code Remove unused fields Add verbosity and quiet options Add support for external config and arguments loading (file, environment) for #33 Handle quiet flag Fix argument handling Unordered vec comparison Remove trait_impl field Add breaking field Show commit info in debug fmt Make item kind part of path (support different-kinded items with identical path) Fix turbofish Update tests to match new notation Fix assoc types in impls Fmt Add feature flag support for Cargo build Update README.md Update README.md Remove old unused deps Add --package parameter for workspaces Fmt Unused import --- .github/workflows/main.yml | 12 + .github/workflows/pr.yml | 12 + Cargo.lock | 467 ++++++++++-- Cargo.toml | 24 +- README.md | 52 +- cargo-breaking.toml | 1 + src/ast.rs | 44 -- src/cli.rs | 31 +- src/cli/config.rs | 173 +++++ src/comparator.rs | 362 +-------- src/diagnosis.rs | 171 ++--- src/git.rs | 400 ---------- src/glue.rs | 238 ++++-- src/lib.rs | 85 ++- src/manifest.rs | 50 +- src/public_api.rs | 944 +++++++++++++----------- src/public_api/assoc_const.rs | 10 + src/public_api/assoc_type.rs | 11 + src/public_api/functions.rs | 96 +-- src/public_api/imports.rs | 549 -------------- src/public_api/methods.rs | 180 +---- src/public_api/modules.rs | 12 + src/public_api/trait_defs.rs | 231 +----- src/public_api/trait_impls.rs | 217 +----- src/public_api/types.rs | 385 +++------- src/public_api/utils.rs | 42 -- src/rustdoc.rs | 69 ++ src/rustdoc/types.rs | 760 +++++++++++++++++++ tests/enum.rs | 54 +- tests/function.rs | 89 +-- tests/method.rs | 52 +- tests/struct.rs | 244 ++++++ tests/structs.rs | 177 ----- tests/{trait_defs.rs => trait_def.rs} | 36 +- tests/{trait_impls.rs => trait_impl.rs} | 83 ++- 35 files changed, 2985 insertions(+), 3378 deletions(-) create mode 100644 cargo-breaking.toml delete mode 100644 src/ast.rs create mode 100644 src/cli/config.rs delete mode 100644 src/git.rs create mode 100644 src/public_api/assoc_const.rs create mode 100644 src/public_api/assoc_type.rs delete mode 100644 src/public_api/imports.rs create mode 100644 src/public_api/modules.rs delete mode 100644 src/public_api/utils.rs create mode 100644 src/rustdoc.rs create mode 100644 src/rustdoc/types.rs create mode 100644 tests/struct.rs delete mode 100644 tests/structs.rs rename tests/{trait_defs.rs => trait_def.rs} (58%) rename tests/{trait_impls.rs => trait_impl.rs} (51%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0aa6354..798a3b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,6 +18,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: @@ -32,6 +36,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: @@ -46,6 +54,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 42e29cd..da4c247 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,6 +16,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: @@ -42,6 +46,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: @@ -56,6 +64,10 @@ jobs: - uses: Swatinem/rust-cache@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + - name: cargo test uses: actions-rs/cargo@v1 with: diff --git a/Cargo.lock b/Cargo.lock index b0e64d8..7decf84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,12 +3,12 @@ version = 3 [[package]] -name = "ansi_term" -version = "0.11.0" +name = "aho-corasick" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "winapi", + "memchr", ] [[package]] @@ -17,6 +17,15 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" +[[package]] +name = "atomic" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +dependencies = [ + "autocfg", +] + [[package]] name = "atty" version = "0.2.14" @@ -42,15 +51,27 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cargo-breaking" -version = "0.0.3-alpha.0" +version = "0.0.4-alpha.0" dependencies = [ "anyhow", "cargo_toml", "clap", + "derivative", + "env_logger", + "fake", + "figment", + "fs_extra", "git2", + "itertools", + "log", + "once_cell", + "rustdoc-types", "semver", - "syn", + "serde", + "serde_json", "tap", + "tempfile", + "toml", ] [[package]] @@ -81,17 +102,90 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.3" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_lex", + "indexmap", + "lazy_static", "strsim", + "termcolor", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fake" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a8531dd3a64fd1cfbe92fad4160bc2060489c6195fe847e045e5788f710bae" +dependencies = [ + "rand", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "figment" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" +dependencies = [ + "atomic", + "pear", + "serde", + "serde_json", + "toml", + "uncased", + "version_check", ] [[package]] @@ -104,6 +198,23 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "git2" version = "0.13.20" @@ -119,6 +230,12 @@ dependencies = [ "url", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "hermit-abi" version = "0.1.18" @@ -128,6 +245,12 @@ dependencies = [ "libc", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "idna" version = "0.2.3" @@ -139,6 +262,46 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + [[package]] name = "jobserver" version = "0.1.22" @@ -148,11 +311,17 @@ dependencies = [ "libc", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" -version = "0.2.95" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libgit2-sys" @@ -196,9 +365,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -209,6 +378,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + [[package]] name = "openssl-probe" version = "0.1.4" @@ -228,6 +409,35 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + +[[package]] +name = "pear" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -240,24 +450,123 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustdoc-types" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa792ee5551052d6e577cedadec6f7eae744b26db967e8e9fb4c1b604a608c7f" +dependencies = [ + "serde", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + [[package]] name = "semver" version = "1.0.1" @@ -266,36 +575,50 @@ checksum = "d023dabf011d5dcb5ac64e3685d97d3b0ef412911077a2851455c6098524a723" [[package]] name = "serde" -version = "1.0.126" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.72" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -305,14 +628,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "textwrap" -version = "0.11.0" +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "unicode-width", + "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "tinyvec" version = "1.2.0" @@ -330,13 +673,22 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] +[[package]] +name = "uncased" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.5" @@ -346,6 +698,12 @@ dependencies = [ "matches", ] +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + [[package]] name = "unicode-normalization" version = "0.1.18" @@ -355,18 +713,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - [[package]] name = "url" version = "2.2.2" @@ -386,10 +732,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" [[package]] -name = "vec_map" -version = "0.8.2" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" @@ -407,8 +759,23 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index 889c78d..8d4506c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cargo-breaking" -version = "0.0.3-alpha.0" -authors = ["o0Ignition0o "] -edition = "2018" +version = "0.0.4-alpha.0" +authors = ["o0Ignition0o ", "Sasha Pourcelot ", "Tom Niget "] +edition = "2021" description = "checks the diff between your last publish and the current code, and lets you know if there are breaking changes so you can bump to the right version." keywords = ["release", "api", "breaking", "change"] license = "MPL-2.0" @@ -15,13 +15,23 @@ path = "src/lib.rs" path = "src/main.rs" name = "cargo-breaking" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -syn = { version = "1.0", features = ["full", "extra-traits", "visit"] } anyhow = "1.0" git2 = "0.13" cargo_toml = "0.9" semver = "1.0" -clap = "2.33" +clap = { version = "3.1.18", features = ["cargo"] } tap = "1.0" +log = "0.4.17" +tempfile = "3.3.0" +fs_extra = "1.2.0" +fake = "2.4.3" +toml = "0.5.9" +serde_json = "1.0.81" +rustdoc-types = "0.11.0" +itertools = "0.10.3" +env_logger = "0.9.0" +derivative = "2.2.0" +once_cell = "1.12.0" +serde = "1.0.137" +figment = { version = "0.10.6", features = ["toml", "json", "env"] } \ No newline at end of file diff --git a/README.md b/README.md index 0f42d40..9a46d51 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ # `cargo-breaking`
- -
- -Logo is provided by Morgane Gaillard (@Arlune) under the MPL license. +

+ build status + crates.io version + download count +

+ +
+ Logo is provided by Morgane Gaillard (@Arlune) under the MPL license. +
+ +
@@ -14,33 +21,33 @@ shows what changed, and suggests the next version according to [semver][semver]. ## Example Suppose you're building a crate that, for any reason, deals with users. The -crate version is 2.4.3. You remove the `User::from_str` method, add a new public -field to `User`, implement `Debug` for it and add the `User::from_path` -function. +crate version is 2.4.3. You remove the `User::from_str` method, change the type of +a public field, implement `Debug` for it, add the `User::from_path` +function, and deprecate the `User::from_foo` function. When invoked, the following text should be printed: ```none $ cargo breaking -- user::User::from_str -≠ user::User -+ user::User::from_path -+ user::User: Debug +- user::User::from_str (method) +≠ user::User::some_field (struct field) ++ user::User::from_path (method) ++ user::User::[impl Debug] (impl) +⚠ user::User::from_foo (method) Next version is: 3.0.0 ``` ### Args -`against`, an arg to specify the github ref (a tag, a branch name or a commit) against which we can compare our current crate version. - -- use: - -```none -cargo breaking -a branch_name -``` - -- default: "main" +| Argument | Description | Example | +|-------------------------|--------------------------------------------------------------|---------------------------| +| `--against` / `-a` | The Git reference of the source code to be compared against. | `-a develop/foo-feature` | +| `--verbose` / `-v` | Logging level: Off, Error, Warn, Info, Debug, Trace. | `-vvv` (Info level) | +| `--quiet` / `-q` | Hide build output except on failure. | | +| `--features` / `-F` | Space-separated list of features to activate. | `-F alt_impls win32 json` | +| `--all-features` | Activate all available features. | | +| `--no-default-features` | Don't activate the `default` feature. | | ## Goals and non goals @@ -54,7 +61,7 @@ to ignore the most subtle ones. This includes, but is not limited to: ## Status By default, `cargo-breaking` compares the public API of the crate against what -is exposed in the `main` branch. This can be changed with the `--against` +is exposed the `main` branch. This can be changed with the `--against` (abbreviated by `-a`) parameter. The value can be a branch name, a tag name, or a commit SHA-1. @@ -63,7 +70,8 @@ It currently detects the following: - functions, - struct fields and generic parameters, - enum variants, fields and generic parameters, -- methods when the implemented type is simple enough. +- implementations, including methods and associated items, +- trait definitions. As we compare parts of the crate AST, it reports a lot of false positives: diff --git a/cargo-breaking.toml b/cargo-breaking.toml new file mode 100644 index 0000000..2b0e8ca --- /dev/null +++ b/cargo-breaking.toml @@ -0,0 +1 @@ +verbosity="Trace" \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs deleted file mode 100644 index 0f1a8e3..0000000 --- a/src/ast.rs +++ /dev/null @@ -1,44 +0,0 @@ -use syn::{ - parse::{Parse, ParseStream, Result as ParseResult}, - Error as SynError, File, -}; - -use std::str::FromStr; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct CrateAst(pub File); - -impl CrateAst { - pub(crate) fn ast(&self) -> &File { - &self.0 - } -} - -impl FromStr for CrateAst { - type Err = SynError; - - fn from_str(s: &str) -> Result { - syn::parse_str(s).map(CrateAst) - } -} - -impl Parse for CrateAst { - fn parse(input: ParseStream) -> ParseResult { - Ok(CrateAst(input.parse()?)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_simple_crate() { - assert!(CrateAst::from_str("fn a() {}").is_ok()); - } - - #[test] - fn syntax_error_case() { - assert!(CrateAst::from_str("fnn a() {}").is_err()); - } -} diff --git a/src/cli.rs b/src/cli.rs index b77e5e7..ef68c36 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,30 +1 @@ -use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; - -pub(crate) struct ProgramConfig { - pub comparaison_ref: String, -} - -impl ProgramConfig { - pub(crate) fn parse() -> ProgramConfig { - let matches = App::new(crate_name!()) - .version(crate_version!()) - .author(crate_authors!()) - .about(crate_description!()) - .arg( - Arg::with_name("crate_name") - .required(false) - ) - .arg( - Arg::with_name("against") - .short("a") - .help("Sets the git reference to compare the API against. Can be a tag, a branch name or a commit.") - .takes_value(true) - .required(false) - .default_value("main") - ).get_matches(); - - let comparaison_ref = matches.value_of("against").unwrap().to_owned(); - - ProgramConfig { comparaison_ref } - } -} +pub mod config; diff --git a/src/cli/config.rs b/src/cli/config.rs new file mode 100644 index 0000000..1275f6d --- /dev/null +++ b/src/cli/config.rs @@ -0,0 +1,173 @@ +use clap::{ + arg, crate_authors, crate_description, crate_name, crate_version, Arg, ArgMatches, Command, +}; +use figment::providers::{Env, Format, Json, Serialized, Toml}; +use figment::value::{Dict, Map}; +use figment::{Error, Figment, Metadata, Profile, Provider}; +use log::LevelFilter; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; + +static CONFIG: Lazy = Lazy::new(ProgramConfig::parse); + +pub fn get() -> &'static ProgramConfig { + &CONFIG +} + +#[repr(usize)] +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] +pub enum LogLevel { + Off, + Error, + Warn, + Info, + Debug, + Trace, +} + +impl From for LevelFilter { + fn from(level: LogLevel) -> Self { + match level { + LogLevel::Off => LevelFilter::Off, + LogLevel::Error => LevelFilter::Error, + LogLevel::Warn => LevelFilter::Warn, + LogLevel::Info => LevelFilter::Info, + LogLevel::Debug => LevelFilter::Debug, + LogLevel::Trace => LevelFilter::Trace, + } + } +} + +impl From for LogLevel { + fn from(level: u64) -> Self { + match level { + 0 => LogLevel::Off, + 1 => LogLevel::Error, + 2 => LogLevel::Warn, + 3 => LogLevel::Info, + 4 => LogLevel::Debug, + _ => LogLevel::Trace, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProgramConfig { + pub comparison_ref: String, + pub display_build_output: bool, + pub verbosity: LogLevel, + pub features: Vec, + pub all_features: bool, + pub no_default_features: bool, + pub package: Option, +} + +impl Default for ProgramConfig { + fn default() -> Self { + ProgramConfig { + comparison_ref: "main".to_string(), + display_build_output: true, + verbosity: LogLevel::Error, + features: vec![], + all_features: false, + no_default_features: false, + package: None, + } + } +} + +struct ClapProvider(ArgMatches); + +impl Provider for ClapProvider { + fn metadata(&self) -> Metadata { + Metadata::named("clap") + } + + fn data(&self) -> Result, Error> { + let profile = Profile::default(); + let mut res = Dict::new(); + + if let Some(against) = self.0.value_of("against") { + res.insert("comparison_ref".into(), against.into()); + } + + let verbosity = self.0.occurrences_of("verbose"); + if verbosity > 0 { + res.insert( + "verbosity".into(), + format!("{:?}", LogLevel::from(verbosity)).into(), + ); + } + + if self.0.is_present("quiet") { + res.insert("display_build_output".into(), false.into()); + } + + if self.0.is_present("features") { + let features = self.0.values_of("features").unwrap(); + res.insert("features".into(), features.collect::>().into()); + } + + if self.0.is_present("all-features") { + res.insert("all_features".into(), true.into()); + } + + if self.0.is_present("no-default-features") { + res.insert("no_default_features".into(), true.into()); + } + + if let Some(package) = self.0.value_of("package") { + res.insert("package".into(), package.into()); + } + + Ok(profile.collect(res)) + } +} + +impl ProgramConfig { + pub(crate) fn parse() -> ProgramConfig { + let matches = Command::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .arg(Arg::new("verbose") + .short('v') + .multiple_occurrences(true) + .max_occurrences(LevelFilter::max() as usize) + .help("Increase verbosity of output")) + .arg(Arg::new("quiet") + .short('q') + .help("Hide build output except in case of error")) + .arg(Arg::new("features") + .short('F') + .help("Space-separated list of features to activate") + .required(false) + .multiple_values(true) + .multiple_occurrences(true) + .takes_value(true)) + .arg( + Arg::new("against") + .short('a') + .help("The Git reference of the source code to be compared against. Can be a tag, a branch name, a commit, an ancestry reference or any other valid Git reference.") + .takes_value(true) + .required(false) + ) + .arg(arg!(-p --package "Package to compare") + .required(false)) + .arg(arg!(--"all-features" "Activate all available features")) + .arg(arg!(--"no-default-features" "Don't activate the `default` feature")) + .allow_external_subcommands(true) + .get_matches(); + + Figment::from(ClapProvider(matches)) + .join(Env::prefixed("CARGO_BREAKING_")) + .join(Toml::file("cargo-breaking.toml")) + .join(Json::file("cargo-breaking.json")) + .join(Serialized::defaults(ProgramConfig::default())) + .extract() + .unwrap_or_else(|e| { + eprintln!("Failed to load configuration: {}", e); + std::process::exit(1); + }) + } +} diff --git a/src/comparator.rs b/src/comparator.rs index b65dc79..5bcec35 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -1,3 +1,5 @@ +use log::debug; +use std::collections::HashSet; use std::{ collections::HashMap, fmt::{Display, Formatter, Result as FmtResult}, @@ -6,12 +8,6 @@ use std::{ use semver::{BuildMetadata, Prerelease, Version}; -use syn::{ - braced, - parse::{Parse, ParseStream, Result as ParseResult}, - Token, -}; - use crate::{ diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}, public_api::PublicApi, @@ -23,7 +19,7 @@ pub struct ApiComparator { } impl ApiComparator { - pub(crate) fn new(previous: PublicApi, current: PublicApi) -> ApiComparator { + pub fn new(previous: PublicApi, current: PublicApi) -> ApiComparator { ApiComparator { previous, current } } @@ -34,8 +30,7 @@ impl ApiComparator { self.item_modifications(&mut collector); self.item_additions(&mut collector); - let mut diags = collector.finalize(); - diags.sort(); + let diags = collector.finalize(); ApiCompatibilityDiagnostics { diags } } @@ -48,6 +43,7 @@ impl ApiComparator { fn item_modifications(&self, diagnosis_collector: &mut DiagnosisCollector) { map_modifications(self.previous.items(), self.current.items()).for_each( |(path, kind_a, kind_b)| { + debug!("Modification for {}: {:?} != {:?}", path, kind_a, kind_b); kind_a.modification_diagnosis(kind_b, path, diagnosis_collector) }, ) @@ -59,24 +55,6 @@ impl ApiComparator { } } -impl Parse for ApiComparator { - fn parse(input: ParseStream) -> ParseResult { - let previous; - braced!(previous in input); - - input.parse::()?; - - let current; - braced!(current in input); - - if input.peek(Token![,]) { - input.parse::().unwrap(); - } - - Ok(ApiComparator::new(previous.parse()?, current.parse()?)) - } -} - #[derive(Clone, Debug, Default, PartialEq)] pub struct ApiCompatibilityDiagnostics { diags: Vec, @@ -131,9 +109,7 @@ impl ApiCompatibilityDiagnostics { } fn contains_breaking_changes(&self) -> bool { - self.diags - .iter() - .any(|diag| diag.is_removal() || diag.is_modification()) + self.diags.iter().any(|diag| diag.is_breaking()) } fn contains_additions(&self) -> bool { @@ -156,25 +132,17 @@ impl ApiCompatibilityDiagnostics { } } -impl Parse for ApiCompatibilityDiagnostics { - fn parse(input: ParseStream) -> ParseResult { - let comparator = input.parse::()?; - - Ok(comparator.run()) - } -} - -fn map_difference<'a, K, V>( +pub(crate) fn map_difference<'a, K, V>( a: &'a HashMap, b: &'a HashMap, ) -> impl Iterator where K: Eq + Hash, { - a.iter().filter(move |(k, _)| b.get(k).is_none()) + a.iter().filter(move |(k, _v)| b.get(k).is_none()) } -fn map_modifications<'a, K, V>( +pub(crate) fn map_modifications<'a, K, V>( a: &'a HashMap, b: &'a HashMap, ) -> impl Iterator @@ -187,313 +155,9 @@ where .filter(|(_, v1, v2)| v1 != v2) } -#[cfg(test)] -mod tests { - use syn::parse_quote; - - use super::*; - - fn addition_diagnosis() -> DiagnosisItem { - parse_quote! { + foo::bar::baz } - } - - fn modification_diagnosis() -> DiagnosisItem { - parse_quote! { <> foo::bar::baz } - } - - fn removal_diagnosis() -> DiagnosisItem { - parse_quote! { - foo::bar::baz } - } - - macro_rules! compatibility_diag { - ($name:ident: empty) => { - let $name = ApiCompatibilityDiagnostics::default(); - }; - - ($name:ident: removal) => { - let mut $name = ApiCompatibilityDiagnostics::default(); - - $name.diags.push(removal_diagnosis()); - - let $name = $name; - }; - - ($name:ident: modification) => { - let mut $name = ApiCompatibilityDiagnostics::default(); - - $name.diags.push(modification_diagnosis()); - - let $name = $name; - }; - - ($name:ident: addition) => { - let mut $name = ApiCompatibilityDiagnostics::default(); - - $name.diags.push(addition_diagnosis()); - - let $name = $name; - }; - } - - mod api_comparator { - use super::*; - - #[test] - fn removal() { - let comparator: ApiComparator = parse_quote! { - { - mod foo { - mod bar { - pub fn baz(n: usize) {} - } - } - }, - {}, - }; - - let left = comparator.run(); - compatibility_diag!(right: removal); - - assert_eq!(left, right); - } - - #[test] - fn modification() { - let comparator: ApiComparator = parse_quote! { - { - mod foo { - mod bar { - pub fn baz(n: usize) {} - } - } - }, - { - mod foo { - mod bar { - pub fn baz(n: u32) -> u32 {} - } - } - }, - }; - let left = comparator.run(); - compatibility_diag!(right: modification); - - assert_eq!(left, right); - } - } - - mod api_compatibility_diagnostic { - use super::*; - - mod removal { - use super::*; - - #[test] - fn is_breaking() { - compatibility_diag!(comp: removal); - assert!(comp.contains_breaking_changes()); - } - - #[test] - fn is_not_addition() { - compatibility_diag!(comp: removal); - assert!(!comp.contains_additions()); - } +pub(crate) fn cmp_vec_unordered(a: &[T], b: &[T]) -> bool { + let a: HashSet<_> = a.iter().collect(); + let b: HashSet<_> = b.iter().collect(); - #[test] - fn display() { - compatibility_diag!(comp: removal); - assert_eq!(comp.to_string(), "- foo::bar::baz\n"); - } - - #[test] - fn is_not_empty() { - compatibility_diag!(comp: removal); - assert!(!comp.is_empty()); - } - } - - mod modification { - use super::*; - - #[test] - fn is_breaking() { - compatibility_diag!(comp: modification); - assert!(comp.contains_breaking_changes()); - } - - #[test] - fn is_not_addition() { - compatibility_diag!(comp: modification); - assert!(!comp.contains_additions()); - } - - #[test] - fn display() { - compatibility_diag!(comp: modification); - assert_eq!(comp.to_string(), "≠ foo::bar::baz\n"); - } - - #[test] - fn is_not_empty() { - compatibility_diag!(comp: modification); - assert!(!comp.is_empty()); - } - } - - mod addition { - use super::*; - - #[test] - fn is_not_breaking() { - compatibility_diag!(comp: addition); - assert!(!comp.contains_breaking_changes()); - } - - // TODO: rename addition -> non-breaking - #[test] - fn is_addition() { - compatibility_diag!(comp: addition); - assert!(comp.contains_additions()); - } - - #[test] - fn display() { - compatibility_diag!(comp: addition); - assert_eq!(comp.to_string(), "+ foo::bar::baz\n"); - } - - #[test] - fn is_not_empyt() { - compatibility_diag!(comp: addition); - assert!(!comp.is_empty()); - } - } - - mod no_changes { - use super::*; - - #[test] - fn is_not_breaking() { - compatibility_diag!(comp: empty); - assert!(!comp.contains_breaking_changes()); - } - - #[test] - fn is_not_addition() { - compatibility_diag!(comp: empty); - assert!(!comp.contains_additions()); - } - - #[test] - fn is_empty() { - compatibility_diag!(comp: empty); - assert!(comp.is_empty()); - } - } - - mod guess_next_version { - use super::*; - - fn sample_version() -> Version { - Version::parse("3.2.3").unwrap() - } - - fn version_with_prerelease() -> Version { - Version::parse("3.2.3-pre1").unwrap() - } - - fn version_with_build() -> Version { - Version::parse("3.2.3+20160325").unwrap() - } - - #[test] - fn breaking_changes_effects() { - compatibility_diag!(comp_1: removal); - compatibility_diag!(comp_2: modification); - - let comps = [comp_1, comp_2]; - - for comp in &comps { - let next_version = comp.guess_next_version(sample_version()); - assert_eq!(next_version, Version::parse("4.0.0").unwrap()) - } - } - - #[test] - fn additions_effects() { - compatibility_diag!(comp: addition); - - let next_version = comp.guess_next_version(sample_version()); - assert_eq!(next_version, Version::parse("3.3.0").unwrap()); - } - - #[test] - fn no_changes_effects() { - compatibility_diag!(comp: empty); - - let next_version = comp.guess_next_version(sample_version()); - assert_eq!(next_version, Version::parse("3.2.4").unwrap()); - } - - #[test] - fn pre_is_cleared() { - compatibility_diag!(comp: empty); - - let next_version = comp.guess_next_version(version_with_prerelease()); - assert_eq!(next_version, Version::parse("3.2.4").unwrap()); - } - - #[test] - fn build_is_cleared() { - compatibility_diag!(comp: empty); - - let next_version = comp.guess_next_version(version_with_build()); - assert_eq!(next_version, Version::parse("3.2.4").unwrap()) - } - } - - mod map_functions { - use super::*; - - fn bare_hashmap_1() -> HashMap { - let mut tmp = HashMap::new(); - tmp.insert(1, 42); - tmp.insert(2, 101); - tmp.insert(3, 13); - tmp - } - - fn bare_hashmap_2() -> HashMap { - let mut tmp = HashMap::new(); - tmp.insert(1, 13); - tmp.insert(2, 101); - tmp.insert(4, 123); - - tmp - } - - #[test] - fn difference() { - let a = bare_hashmap_1(); - let b = bare_hashmap_2(); - - let mut diff = map_difference(&a, &b).collect::>(); - diff.sort_by_key(|(k, _)| *k); - - assert_eq!(diff, [(&3, &13)]); - } - - #[test] - fn modification() { - let a = bare_hashmap_1(); - let b = bare_hashmap_2(); - - let mut modif = map_modifications(&a, &b).collect::>(); - modif.sort_by_key(|(k, _, _)| *k); - - assert_eq!(modif, [(&1, &42, &13)]); - } - } - } + a == b } diff --git a/src/diagnosis.rs b/src/diagnosis.rs index beb4ad9..ff7c0bb 100644 --- a/src/diagnosis.rs +++ b/src/diagnosis.rs @@ -1,13 +1,5 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use syn::Ident; - -#[cfg(test)] -use syn::{ - parse::{Parse, ParseStream, Result as ParseResult}, - Token, -}; - use crate::public_api::ItemPath; pub struct DiagnosisCollector { @@ -24,13 +16,39 @@ impl DiagnosisCollector { } pub(crate) fn finalize(self) -> Vec { - self.inner + let mut res = self.inner; + + // remove redundant lines + // this removes all lines that are hierarchically under additions or removals + // e.g. if a struct is removed, then this will remove all the lines corresponding + // to the removal of its fields + // so you get `- foo::A` instead of `- foo::A\n- foo::A::field1\n- foo::A::field2` + + // sorting beforehand makes the whole thing linear-time + res.sort_by_cached_key(|item| item.path.clone()); + let mut vec = Vec::with_capacity(res.len()); + let mut prefix: Option = None; + for item in res { + if let Some(p) = &prefix { + if item.path.path.starts_with(&*p.path) && item.path.path.len() > p.path.len() { + continue; + } else { + prefix = None; + } + } + if item.kind == DiagnosisItemKind::Removal || item.kind == DiagnosisItemKind::Addition { + prefix = Some(item.path.clone()); + }; + vec.push(item); + } + vec.sort(); + vec } } pub(crate) trait DiagnosticGenerator { fn removal_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { - collector.add(DiagnosisItem::removal(path.clone(), None)); + collector.add(DiagnosisItem::removal(path.clone())); } fn modification_diagnosis( @@ -39,11 +57,11 @@ pub(crate) trait DiagnosticGenerator { path: &ItemPath, collector: &mut DiagnosisCollector, ) { - collector.add(DiagnosisItem::modification(path.clone(), None)); + collector.add(DiagnosisItem::modification(path.clone())); } fn addition_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { - collector.add(DiagnosisItem::addition(path.clone(), None)); + collector.add(DiagnosisItem::addition(path.clone())); } } @@ -51,40 +69,44 @@ pub(crate) trait DiagnosticGenerator { pub(crate) struct DiagnosisItem { kind: DiagnosisItemKind, path: ItemPath, - trait_impl: Option, + breaking: bool, } impl DiagnosisItem { - pub(crate) fn removal(path: ItemPath, trait_impl: Option) -> DiagnosisItem { + pub(crate) fn removal(path: ItemPath) -> DiagnosisItem { DiagnosisItem { kind: DiagnosisItemKind::Removal, path, - trait_impl, + breaking: true, } } - pub(crate) fn modification(path: ItemPath, trait_impl: Option) -> DiagnosisItem { + pub(crate) fn modification(path: ItemPath) -> DiagnosisItem { DiagnosisItem { kind: DiagnosisItemKind::Modification, path, - trait_impl, + breaking: true, } } - pub(crate) fn addition(path: ItemPath, trait_impl: Option) -> DiagnosisItem { + pub(crate) fn addition(path: ItemPath) -> DiagnosisItem { DiagnosisItem { kind: DiagnosisItemKind::Addition, path, - trait_impl, + breaking: false, } } - pub(crate) fn is_removal(&self) -> bool { - self.kind == DiagnosisItemKind::Removal + pub(crate) fn deprecation(path: ItemPath) -> DiagnosisItem { + DiagnosisItem { + kind: DiagnosisItemKind::Deprecation, + path, + breaking: false, + } } - pub(crate) fn is_modification(&self) -> bool { - self.kind == DiagnosisItemKind::Modification + pub(crate) fn is_breaking(&self) -> bool { + self.breaking } pub(crate) fn is_addition(&self) -> bool { @@ -94,36 +116,7 @@ impl DiagnosisItem { impl Display for DiagnosisItem { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "{} {}", self.kind, self.path)?; - - if let Some(trait_) = &self.trait_impl { - write!(f, ": {}", trait_) - } else { - Ok(()) - } - } -} - -#[cfg(test)] -impl Parse for DiagnosisItem { - fn parse(input: ParseStream) -> ParseResult { - let kind = input.parse()?; - let path = input.parse()?; - - let trait_impl = if input.peek(Token![:]) { - input.parse::().unwrap(); - input.parse::().unwrap(); - - Some(input.parse()?) - } else { - None - }; - - Ok(DiagnosisItem { - kind, - path, - trait_impl, - }) + write!(f, "{} {}", self.kind, self.path) } } @@ -132,78 +125,18 @@ enum DiagnosisItemKind { Removal, Modification, Addition, + Deprecation, } impl Display for DiagnosisItemKind { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use DiagnosisItemKind::*; match self { - DiagnosisItemKind::Removal => '-', - DiagnosisItemKind::Modification => '≠', - DiagnosisItemKind::Addition => '+', + Removal => '-', + Modification => '≠', + Addition => '+', + Deprecation => '⚠', } .fmt(f) } } - -#[cfg(test)] -impl Parse for DiagnosisItemKind { - fn parse(input: ParseStream) -> ParseResult { - if input.peek(Token![-]) { - input.parse::().unwrap(); - Ok(DiagnosisItemKind::Removal) - } else if input.peek(Token![<]) { - input.parse::().unwrap(); - input.parse::]>().unwrap(); - - Ok(DiagnosisItemKind::Modification) - } else if input.peek(Token![+]) { - input.parse::().unwrap(); - Ok(DiagnosisItemKind::Addition) - } else { - Err(input.error("Excepted `-`, `<>` or `+`")) - } - } -} - -#[cfg(test)] -mod tests { - use syn::parse_quote; - - use super::*; - - #[test] - fn display_implementation_standard_removal() { - let diag: DiagnosisItem = parse_quote! { - - foo::baz::Bar - }; - - assert_eq!(diag.to_string(), "- foo::baz::Bar"); - } - - #[test] - fn display_implementation_standard_modification() { - let diag: DiagnosisItem = parse_quote! { - <> foo::Bar - }; - - assert_eq!(diag.to_string(), "≠ foo::Bar"); - } - - #[test] - fn display_implementation_standard_addition() { - let diag: DiagnosisItem = parse_quote! { - + foo::bar::Baz - }; - - assert_eq!(diag.to_string(), "+ foo::bar::Baz"); - } - - #[test] - fn display_implementation_trait_impl() { - let diag: DiagnosisItem = parse_quote! { - <> foo::bar::Baz: impl Foo - }; - - assert_eq!(diag.to_string(), "≠ foo::bar::Baz: Foo"); - } -} diff --git a/src/git.rs b/src/git.rs deleted file mode 100644 index 14908c9..0000000 --- a/src/git.rs +++ /dev/null @@ -1,400 +0,0 @@ -use anyhow::{Context, Result as AnyResult}; - -use git2::{Repository, StashFlags, StatusOptions}; - -pub(crate) trait GitBackend: Sized { - fn run_in(&mut self, id: &str, f: F) -> AnyResult - where - F: FnOnce() -> O, - { - self.switch_to(id) - .with_context(|| format!("Failed to checkout to {}", id))?; - - let rslt = f(); - - self.switch_back().with_context(|| { - format!( - "Failed to switch back to {}", - self.previous_branch().unwrap() - ) - })?; - - Ok(rslt) - } - - fn switch_to(&mut self, id: &str) -> AnyResult<()> { - if self.needs_stash() { - self.stash_push().context("Failed to stash changes")?; - } - - let branch_name = self - .head_name() - .context("Failed to get HEAD name")? - .expect("Not currently on a branch"); - - self.checkout_to(id) - .with_context(|| format!("Failed to checkout to {}", id))?; - self.set_previous_branch(branch_name.as_str()); - - Ok(()) - } - - fn switch_back(&mut self) -> AnyResult<()> { - let previous_branch = self - .previous_branch() - .expect("Previous branch is not set") - .to_owned(); - - self.checkout_to(previous_branch.as_str()) - .with_context(|| format!("Failed to checkout to {}", previous_branch))?; - - if self.needs_stash() { - self.stash_pop() - .context("Failed to restore initial repository state")?; - } - - Ok(()) - } - - fn current() -> AnyResult; - - fn head_name(&self) -> AnyResult>; - fn previous_branch(&self) -> Option<&str>; - fn set_previous_branch(&mut self, name: &str); - - fn needs_stash(&self) -> bool; - fn stash_push(&mut self) -> AnyResult<()>; - fn stash_pop(&mut self) -> AnyResult<()>; - fn checkout_to(&mut self, id: &str) -> AnyResult<()>; -} - -pub(crate) struct CrateRepo { - repo: Repository, - previous_branch_name: Option, - needs_stash: bool, -} - -impl GitBackend for CrateRepo { - fn current() -> AnyResult { - let repo = Repository::open_from_env().context("Failed to open repository")?; - let needs_stash = - CrateRepo::needs_stash(&repo).context("Failed to determine if stash is needed")?; - - Ok(CrateRepo { - repo, - previous_branch_name: None, - needs_stash, - }) - } - - fn head_name(&self) -> AnyResult> { - self.repo - .head() - .map(|r| r.name().map(String::from)) - .map_err(Into::into) - } - - fn previous_branch(&self) -> Option<&str> { - self.previous_branch_name.as_ref().map(AsRef::as_ref) - } - - fn set_previous_branch(&mut self, name: &str) { - assert!( - self.previous_branch_name.is_none(), - "set_previous_branch is called when a previous branch is already set" - ); - self.previous_branch_name = Some(name.to_owned()); - } - - fn needs_stash(&self) -> bool { - self.needs_stash - } - - fn stash_push(&mut self) -> AnyResult<()> { - let stash_options = StashFlags::INCLUDE_UNTRACKED; - let signature = self - .repo - .signature() - .context("Failed to create user signature")?; - - self.repo - .stash_save2(&signature, None, Some(stash_options)) - .map(drop) - .map_err(Into::into) - } - - fn stash_pop(&mut self) -> AnyResult<()> { - self.repo - .stash_pop(0, None) - .context("Failed to pop the stashed state") - } - - fn checkout_to(&mut self, id: &str) -> AnyResult<()> { - let (obj, reference) = self - .repo - .revparse_ext(id) - .with_context(|| format!("Failed to get object corresponding to {}", id))?; - - self.repo - .checkout_tree(&obj, None) - .with_context(|| format!("Failed to change the tree to {}", id))?; - - match reference { - Some(gref) => self - .repo - .set_head(gref.name().expect("Branch name must be UTF-8")) - .with_context(|| format!("Failed to set head to {}", gref.name().unwrap()))?, - - None => self.repo.set_head_detached(obj.id())?, - } - - Ok(()) - } -} - -impl CrateRepo { - fn needs_stash(repo: &Repository) -> AnyResult { - let mut options = StatusOptions::new(); - let options = options.include_untracked(true); - - let statuses = repo - .statuses(Some(options)) - .context("Failed to get current repository status")?; - - Ok(!statuses.is_empty()) - } -} - -#[cfg(test)] -use std::{cell::RefCell, rc::Rc}; - -#[cfg(test)] -#[allow(clippy::type_complexity)] -struct FakeRepo<'a> { - on_head_name: Box AnyResult> + 'a>, - on_previous_branch: Box Fn(&'b FakeRepo) -> Option<&'b str> + 'a>, - on_set_previous_branch: Rc, &str) + 'a>, - on_needs_stash: Box) -> bool + 'a>, - on_stash_push: Rc) -> AnyResult<()> + 'a>, - on_stash_pop: Rc) -> AnyResult<()> + 'a>, - on_checkout_to: Rc, &str) -> AnyResult<()> + 'a>, - - actions: RefCell>, -} - -#[cfg(test)] -impl<'a> GitBackend for FakeRepo<'a> { - fn current() -> AnyResult { - Ok(FakeRepo::new()) - } - - fn head_name(&self) -> AnyResult> { - self.add_action(FakeRepoCall::HeadName); - (self.on_head_name)(self) - } - - fn previous_branch(&self) -> Option<&str> { - self.add_action(FakeRepoCall::PreviousBranch); - (self.on_previous_branch)(self) - } - - fn set_previous_branch(&mut self, name: &str) { - self.add_action(FakeRepoCall::SetPreviousBranch(name.to_owned())); - self.on_set_previous_branch.clone()(self, name) - } - - fn needs_stash(&self) -> bool { - self.add_action(FakeRepoCall::NeedsStash); - (self.on_needs_stash)(self) - } - - fn stash_push(&mut self) -> AnyResult<()> { - self.add_action(FakeRepoCall::StashPush); - self.on_stash_push.clone()(self) - } - - fn stash_pop(&mut self) -> AnyResult<()> { - self.add_action(FakeRepoCall::StashPop); - self.on_stash_pop.clone()(self) - } - - fn checkout_to(&mut self, id: &str) -> AnyResult<()> { - self.add_action(FakeRepoCall::CheckoutTo(id.to_owned())); - self.on_checkout_to.clone()(self, id) - } -} - -#[cfg(test)] -impl<'a> FakeRepo<'a> { - fn new() -> FakeRepo<'a> { - FakeRepo { - on_head_name: Box::new(|_| panic!("`on_head_name` is called but not set")), - on_previous_branch: Box::new(|_| panic!("`on_previous_branch` is called but not set")), - on_set_previous_branch: Rc::new(|_, _| { - panic!("`on_set_previous_branch` is called but not set") - }), - on_needs_stash: Box::new(|_| panic!("`on_needs_stash` is called but not set")), - on_stash_push: Rc::new(|_| panic!("`on_stash_push` is called but not set")), - on_stash_pop: Rc::new(|_| panic!("`on_stash_pop` is called but not set")), - on_checkout_to: Rc::new(|_, _| panic!("`on_checkout_to` is called but not set")), - - actions: RefCell::new(Vec::new()), - } - } - - fn on_head_name( - mut self, - f: impl Fn(&FakeRepo) -> AnyResult> + 'a, - ) -> FakeRepo<'a> { - self.on_head_name = Box::new(f); - self - } - - fn on_previous_branch( - mut self, - f: impl for<'b> Fn(&'b FakeRepo) -> Option<&'b str> + 'a, - ) -> FakeRepo<'a> { - self.on_previous_branch = Box::new(f); - self - } - - fn on_set_previous_branch(mut self, f: impl Fn(&mut FakeRepo<'a>, &str) + 'a) -> FakeRepo<'a> { - self.on_set_previous_branch = Rc::new(f); - self - } - - fn on_needs_stash(mut self, f: impl Fn(&FakeRepo<'a>) -> bool + 'a) -> FakeRepo<'a> { - self.on_needs_stash = Box::new(f); - self - } - - fn on_stash_push( - mut self, - f: impl Fn(&mut FakeRepo<'a>) -> AnyResult<()> + 'a, - ) -> FakeRepo<'a> { - self.on_stash_push = Rc::new(f); - self - } - - fn on_stash_pop(mut self, f: impl Fn(&mut FakeRepo<'a>) -> AnyResult<()> + 'a) -> FakeRepo<'a> { - self.on_stash_pop = Rc::new(f); - self - } - - fn on_checkout_to( - mut self, - f: impl Fn(&mut FakeRepo<'a>, &str) -> AnyResult<()> + 'a, - ) -> FakeRepo<'a> { - self.on_checkout_to = Rc::new(f); - self - } - - fn add_action(&self, a: FakeRepoCall) { - self.actions.borrow_mut().push(a); - } -} - -#[cfg(test)] -#[derive(Clone, Debug, PartialEq)] -enum FakeRepoCall { - HeadName, - PreviousBranch, - SetPreviousBranch(String), - NeedsStash, - StashPush, - StashPop, - CheckoutTo(String), -} - -#[cfg(test)] -mod tests { - use super::*; - - mod switch_to_default_impl { - use super::*; - - #[test] - fn when_stash() { - let mut repo = FakeRepo::new() - .on_needs_stash(|_| true) - .on_stash_push(|_| Ok(())) - .on_head_name(|_| Ok(Some("foo".to_owned()))) - .on_checkout_to(|_, _| Ok(())) - .on_set_previous_branch(|_, _| ()); - - repo.switch_to("bar").unwrap(); - - let expected_calls = [ - FakeRepoCall::NeedsStash, - FakeRepoCall::StashPush, - FakeRepoCall::HeadName, - FakeRepoCall::CheckoutTo("bar".to_owned()), - FakeRepoCall::SetPreviousBranch("foo".to_owned()), - ]; - - assert_eq!(repo.actions.borrow().as_ref(), expected_calls); - } - - #[test] - fn when_non_stash() { - let mut repo = FakeRepo::new() - .on_needs_stash(|_| false) - .on_head_name(|_| Ok(Some("bar".to_owned()))) - .on_checkout_to(|_, _| Ok(())) - .on_set_previous_branch(|_, _| ()); - - repo.switch_to("baz").unwrap(); - - let expected_calls = [ - FakeRepoCall::NeedsStash, - FakeRepoCall::HeadName, - FakeRepoCall::CheckoutTo("baz".to_owned()), - FakeRepoCall::SetPreviousBranch("bar".to_owned()), - ]; - - assert_eq!(repo.actions.borrow().as_ref(), expected_calls); - } - } - - mod go_back_default_impl { - use super::*; - - #[test] - fn when_stash() { - let mut repo = FakeRepo::new() - .on_previous_branch(|_| Some("bar")) - .on_checkout_to(|_, _| Ok(())) - .on_needs_stash(|_| true) - .on_stash_pop(|_| Ok(())); - - repo.switch_back().unwrap(); - - let expected_calls = [ - FakeRepoCall::PreviousBranch, - FakeRepoCall::CheckoutTo("bar".to_owned()), - FakeRepoCall::NeedsStash, - FakeRepoCall::StashPop, - ]; - - assert_eq!(repo.actions.borrow().as_ref(), expected_calls); - } - - #[test] - fn when_non_stash() { - let mut repo = FakeRepo::new() - .on_previous_branch(|_| Some("bar")) - .on_checkout_to(|_, _| Ok(())) - .on_needs_stash(|_| false); - - repo.switch_back().unwrap(); - - let expected_calls = [ - FakeRepoCall::PreviousBranch, - FakeRepoCall::CheckoutTo("bar".to_owned()), - FakeRepoCall::NeedsStash, - ]; - - assert_eq!(repo.actions.borrow().as_ref(), expected_calls); - } - } -} diff --git a/src/glue.rs b/src/glue.rs index de780c3..bdca793 100644 --- a/src/glue.rs +++ b/src/glue.rs @@ -1,75 +1,201 @@ -use std::{ - error::Error, - fmt::{Display, Formatter, Result as FmtResult}, - process::Command, - str::FromStr, -}; - -use anyhow::{bail, Context, Result as AnyResult}; -use syn::Error as SynError; - -use crate::{ast::CrateAst, comparator::ApiComparator, public_api::PublicApi}; - -pub(crate) fn extract_api() -> AnyResult { - let output = Command::new("cargo") - .arg("+nightly") - .arg("rustc") - .arg("--lib") - .arg("--") - .args(&["-Z", "unpretty=expanded"]) - .args(&["-Z", "unpretty=everybody_loops"]) - .arg("--emit=mir") - .output() - .context("Failed to run `cargo rustc`")?; - - if !output.status.success() { - let stderr = String::from_utf8(output.stderr) - .map_err(|_| InvalidRustcOutputEncoding) - .context("Failed to get rustc error message")?; - bail!(stderr); - } +use std::{fs, path::Path}; + +use fake::{faker::lorem::raw::Word, locales::EN, Fake}; + +use anyhow::{Context, Error as AnyError, Result as AnyResult}; - let expanded_code = String::from_utf8(output.stdout) - .map_err(|_| InvalidRustcOutputEncoding) - .context("Failed to get rustc-expanded crate code")?; +use fs_extra::dir::{self, CopyOptions}; +use git2::{ObjectType, Repository}; - let ast = CrateAst::from_str(&expanded_code) - .map_err(InvalidRustcAst) - .context("Failed to parse rustc-provided crate AST")?; +use crate::{rustdoc, ApiComparator, PublicApi}; +use log::{debug, info}; - let api = PublicApi::from_ast(&ast); +use tempfile::TempDir; - Ok(api) +pub(crate) const PREVIOUS_CRATE_NAME: &str = "previous"; +pub(crate) const NEXT_CRATE_NAME: &str = "next"; +pub(crate) const RAW_PACKAGE_NAME: &str = "raw_package"; + +#[derive(Debug)] +pub(crate) struct GlueCrateGenerator { + code_provider: CrateCodeProvider, + package_name: String, + previous_crate_name: String, + next_crate_name: String, + temp_dir: TempDir, } -#[derive(Clone, Copy, Debug, PartialEq)] -struct InvalidRustcOutputEncoding; +#[derive(Debug)] +enum CrateCodeProvider { + Git { comparison_ref: String }, + Raw { previous: String, next: String }, +} -impl Display for InvalidRustcOutputEncoding { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "rustc yielded non-UTF-8 output") +enum Version { + Previous, + Next, +} + +impl CrateCodeProvider { + fn restore_code(&self, dest: &Path, version: Version) -> AnyResult<()> { + info!( + "Copying code for for {} version", + match version { + Version::Previous => "previous", + Version::Next => "next", + } + ); + + match self { + CrateCodeProvider::Git { comparison_ref } => { + dir::copy(Path::new(".git"), &dest, &CopyOptions::new()) + .map(drop) + .context("Failed to copy crate content")?; + + let repo = Repository::open(&dest).context("Failed to open repository")?; + let head = match version { + Version::Next => repo.head()?.peel(ObjectType::Any)?, + Version::Previous => { + repo.revparse_ext(comparison_ref) + .with_context(|| { + format!("Failed to get object corresponding to {}", comparison_ref) + })? + .0 + } + }; + + debug!( + "Checking out {}", + match head.as_tag() { + Some(tag) => tag.name().unwrap_or("").to_string(), + None => head.id().to_string(), + } + ); + + repo.reset(&head, git2::ResetType::Hard, None) + .context("Failed to checkout head") + } + CrateCodeProvider::Raw { previous, next } => { + let src = match version { + Version::Previous => previous, + Version::Next => next, + }; + + let out_dir = dest.join("src"); + fs::create_dir_all(out_dir.as_path()).context("Failed to create code directory")?; + + fs::write(out_dir.join("lib.rs"), src).context("Failed to write raw code file")?; + fs::write( + dest.join("Cargo.toml"), + format!( + r#"[package] +name = "{}" +version = "0.0.1" +[dependencies] +"#, + RAW_PACKAGE_NAME + ), + ) + .context("Failed to write Cargo.toml") + } + } } } -impl Error for InvalidRustcOutputEncoding {} +impl GlueCrateGenerator { + fn new(package_name: String, code_provider: CrateCodeProvider) -> AnyResult { + // We want to avoid any name clash with an actual crate (that can be a dependency) + let random_suffix: &str = Word(EN).fake(); + + let previous_crate_name = format!("{}{}", PREVIOUS_CRATE_NAME, random_suffix); + let next_crate_name = format!("{}{}", NEXT_CRATE_NAME, random_suffix); + + let temp_dir = tempfile::tempdir() + .map_err(AnyError::new) + .context("Failed to create temporary directory")?; + + Ok(GlueCrateGenerator { + code_provider, + package_name, + previous_crate_name, + next_crate_name, + temp_dir, + }) + } + + pub(crate) fn from_git(package_name: String, comparison_ref: &str) -> AnyResult { + GlueCrateGenerator::new( + package_name, + CrateCodeProvider::Git { + comparison_ref: comparison_ref.to_string(), + }, + ) + } + + pub(crate) fn from_code(previous: String, next: String) -> AnyResult { + GlueCrateGenerator::new( + RAW_PACKAGE_NAME.to_string(), + CrateCodeProvider::Raw { previous, next }, + ) + } + + // Returns the root path in which the glue crate is generated + pub(crate) fn generate(self) -> AnyResult { + self.generate_versions() + .context("Failed to generate crates")?; + + Ok(GlueCrate { + package_name: self.package_name.replace('-', "_"), + temp_dir: self.temp_dir, + previous_crate_name: self.previous_crate_name, + next_crate_name: self.next_crate_name, + }) + } + + fn generate_versions(&self) -> AnyResult<()> { + // copy the latest changes + self.generate_version(self.next_crate_name.as_str(), Version::Next) + .context("Failed to generated `next` crate")?; + + // copy the previous version + self.generate_version(self.previous_crate_name.as_str(), Version::Previous) + .context("Failed to generated `previous` crate") + } -#[derive(Clone, Debug)] -struct InvalidRustcAst(SynError); + fn generate_version(&self, crate_name: &str, version: Version) -> AnyResult<()> { + let dest_buf = self.temp_dir.path().join(crate_name); + let dest = dest_buf.as_path(); + fs::create_dir_all(&dest).context("Failed to create destination directory")?; -impl Display for InvalidRustcAst { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "rustc yielded an invalid program: {}", self.0) + self.code_provider.restore_code(dest, version) } } -impl Error for InvalidRustcAst {} +#[derive(Debug)] +pub(crate) struct GlueCrate { + package_name: String, + temp_dir: TempDir, + previous_crate_name: String, + next_crate_name: String, +} -pub fn compare(prev: &str, curr: &str) -> AnyResult { - let prev_ast = CrateAst::from_str(prev).context("Failed to parse code for previous version")?; - let curr_ast = CrateAst::from_str(curr).context("Failed to parse code for current version")?; +impl GlueCrate { + pub(crate) fn path(&self) -> &Path { + self.temp_dir.path() + } - let prev_api = PublicApi::from_ast(&prev_ast); - let curr_api = PublicApi::from_ast(&curr_ast); + fn get_api(&self, crate_name: &str) -> AnyResult { + let crate_path = self.path().join(crate_name); + let crate_data = + rustdoc::run(&crate_path, &self.package_name).context("Failed run rustdoc")?; - Ok(ApiComparator::new(prev_api, curr_api)) + PublicApi::from_crate(&crate_data, 0) + } + + pub(crate) fn build_comparator(&self) -> AnyResult { + Ok(ApiComparator::new( + self.get_api(self.previous_crate_name.as_str())?, + self.get_api(self.next_crate_name.as_str())?, + )) + } } diff --git a/src/lib.rs b/src/lib.rs index 8c4b47d..c116af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,44 +1,95 @@ -mod ast; mod cli; mod comparator; mod diagnosis; -mod git; mod glue; mod manifest; mod public_api; +mod rustdoc; use anyhow::{Context, Result as AnyResult}; +use clap::{crate_name, crate_version}; pub use comparator::ApiCompatibilityDiagnostics; -pub use glue::compare; -use crate::{ - comparator::ApiComparator, - git::{CrateRepo, GitBackend}, -}; +use log::{debug, info}; + +use crate::comparator::ApiComparator; +use crate::glue::GlueCrateGenerator; +use crate::manifest::Manifest; +use crate::public_api::PublicApi; pub fn run() -> AnyResult<()> { - let config = cli::ProgramConfig::parse(); + let config = cli::config::get(); + + env_logger::builder() + .filter_level(config.verbosity.into()) + .init(); + + info!( + "This is {} {}, welcome onboard", + crate_name!(), + crate_version!() + ); + debug!("{:?}", config); - let mut repo = CrateRepo::current().context("Failed to fetch repository data")?; + let manifest = + Manifest::from_env().context("Failed to get information from the manifest file")?; - let version = manifest::get_crate_version().context("Failed to get crate version")?; + info!("Processing crate {}", manifest.package_name()); - let current_api = glue::extract_api().context("Failed to get crate API")?; + let version = manifest.version(); + info!("Detected version: {}", version); - let previous_api = repo.run_in(config.comparaison_ref.as_str(), || { - glue::extract_api().context("Failed to get crate API") - })??; + debug!("Retrieving versions"); + let glue_crate = + GlueCrateGenerator::from_git(manifest.package_name().to_string(), &config.comparison_ref)? + .generate() + .context("Failed to generate glue crate")?; - let api_comparator = ApiComparator::new(previous_api, current_api); + debug!("Setting up the comparator"); + let api_comparator = glue_crate.build_comparator()?; + debug!("Comparing versions"); let diagnosis = api_comparator.run(); - if !diagnosis.is_empty() { + if diagnosis.is_empty() { + println!("No breaking changes found"); + } else { println!("{}", diagnosis); } - let next_version = diagnosis.guess_next_version(version); + let next_version = diagnosis.guess_next_version(version.clone()); println!("Next version is: {}", next_version); Ok(()) } + +pub mod tests { + use crate::{ApiCompatibilityDiagnostics, GlueCrateGenerator}; + use anyhow::Context; + + pub fn diff_from_str(src1: &str, src2: &str) -> ApiCompatibilityDiagnostics { + GlueCrateGenerator::from_code(src1.to_string(), src2.to_string()) + .unwrap() + .generate() + .context("Failed to generate test glue crate") + .unwrap() + .build_comparator() + .context("Unable to build comparator") + .unwrap() + .run() + } + + #[macro_export] + macro_rules! get_diff { + ({$($src1:tt)*}, {$($src2:tt)*}$(,)?) => { + { + let src1 = stringify!($($src1)?); + let src2 = stringify!($($src2)?); + + $crate::tests::diff_from_str(src1, src2) + } + } + } + + pub use get_diff; +} diff --git a/src/manifest.rs b/src/manifest.rs index abb0b68..690e3a8 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,24 +1,42 @@ -use std::path::Path; - +use crate::cli::config; use anyhow::{bail, Context, Result as AnyResult}; -use cargo_toml::Manifest; use semver::Version; +use std::path::{Path, PathBuf}; -pub(crate) fn get_crate_version() -> AnyResult { - let m = load_manifest()?; - get_version_from_manifest(&m).context("Failed to get version from crate manifest") +pub(crate) struct Manifest { + package_name: String, + version: Version, } -fn load_manifest() -> AnyResult { - let p = Path::new("Cargo.toml"); - Manifest::from_path(p).context("Failed to load crate manifest") -} +impl Manifest { + pub(crate) fn from_env() -> AnyResult { + let mut toml_path = PathBuf::from("Cargo.toml"); + if let Some(package) = &config::get().package { + toml_path = Path::new(package).join(toml_path); + } + let initial_manifest = + cargo_toml::Manifest::from_path(toml_path).context("Failed to read manifest file")?; + + let package = match initial_manifest.package { + Some(package) => package, + None => bail!("Excepted a package, found a workspace"), + }; + + let package_name = package.name; + let version = + Version::parse(package.version.as_str()).context("Failed to parse package version")?; + + Ok(Manifest { + package_name, + version, + }) + } -fn get_version_from_manifest(m: &Manifest) -> AnyResult { - let unparsed_version = match &m.package { - Some(package) => &package.version, - None => bail!("Expected a package, found a workspace"), - }; + pub(crate) fn package_name(&self) -> &str { + &self.package_name + } - Version::parse(unparsed_version.as_str()).context("Failed to parser version string") + pub(crate) fn version(&self) -> &Version { + &self.version + } } diff --git a/src/public_api.rs b/src/public_api.rs index 5d3d88c..0b89cb1 100644 --- a/src/public_api.rs +++ b/src/public_api.rs @@ -1,68 +1,326 @@ +mod assoc_const; +mod assoc_type; mod functions; -mod imports; mod methods; +mod modules; mod trait_defs; mod trait_impls; mod types; -mod utils; +use anyhow::{bail, Context, Error as AnyError, Result as AnyResult}; +use itertools::Itertools; +use rustdoc_types::{Crate, Id, Item, ItemEnum, Variant}; use std::{ collections::HashMap, fmt::{Display, Formatter, Result as FmtResult}, }; -use syn::{ - parse::{Parse, ParseStream, Result as ParseResult}, - visit::Visit, - Ident, -}; - -#[cfg(test)] -use syn::Token; - use tap::Tap; -use crate::{ - ast::CrateAst, - diagnosis::{DiagnosisCollector, DiagnosticGenerator}, -}; +use crate::diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}; +use crate::public_api::assoc_const::AssocConstMetadata; +use crate::public_api::assoc_type::AssocTypeMetadata; +use crate::public_api::modules::ModuleMetadata; +use crate::public_api::trait_defs::TraitDefMetadata; +use crate::public_api::trait_impls::TraitImplMetadata; +use crate::public_api::types::{EnumVariantMetadata, TypeFieldMetadata, TypeMetadata}; +use crate::rustdoc::types::RustdocToCb; -use self::{ - functions::{FnPrototype, FnVisitor}, - imports::PathResolver, - methods::{MethodMetadata, MethodVisitor}, - trait_defs::{TraitDefMetadata, TraitDefVisitor}, - trait_impls::TraitImplVisitor, - types::{TypeMetadata, TypeVisitor}, -}; +use self::{functions::FnPrototype, methods::MethodMetadata}; #[derive(Clone, Debug, PartialEq)] -pub(crate) struct PublicApi { +pub struct PublicApi { items: HashMap, } impl PublicApi { - pub(crate) fn from_ast(program: &CrateAst) -> PublicApi { - let resolver = PathResolver::new(program); + fn new(items: HashMap) -> PublicApi { + PublicApi { items } + } - let mut type_visitor = TypeVisitor::new(); - type_visitor.visit_file(program.ast()); + pub(crate) fn from_crate(data: &Crate, current: u32) -> AnyResult { + let items: Result, AnyError> = data + .index + .values() + .filter(|item| item.crate_id == current) // only analyze public items from the crate we're processing + .map(|item| { + let mut res = Vec::new(); + if let Some(summary) = data.paths.get(&item.id) { + // get path of item; skip first item (crate name) + let path = ItemPath::new(summary.path[1..].to_vec(), summary.kind.to_cb(data)); + Self::process_item(data, &mut res, &path, item)?; + Ok(res) + } else { + Ok(vec![]) + } + }) + .flatten_ok() + .collect(); - let mut method_visitor = MethodVisitor::new(type_visitor.types(), &resolver); - method_visitor.visit_file(program.ast()); + items.map(PublicApi::new) + } - let mut fn_visitor = FnVisitor::new(method_visitor.items()); - fn_visitor.visit_file(program.ast()); + fn process_item( + data: &Crate, + res: &mut Vec<(ItemPath, ItemKind)>, + path: &ItemPath, + item: &Item, + ) -> AnyResult<()> { + let kind = match &item.inner { + ItemEnum::Function(f) => ItemKindData::Fn(f.to_cb(data)), + ItemEnum::Struct(s) => { + Self::process_fields(data, res, path, &s.fields)?; + Self::process_impls(data, res, path, &s.impls)?; + ItemKindData::Type(s.to_cb(data)) + } + ItemEnum::Union(u) => { + Self::process_fields(data, res, path, &u.fields)?; + Self::process_impls(data, res, path, &u.impls)?; + ItemKindData::Type(u.to_cb(data)) + } + ItemEnum::Enum(e) => { + Self::process_variants(data, res, path, &e.variants)?; + Self::process_impls(data, res, path, &e.impls)?; + ItemKindData::Type(e.to_cb(data)) + } + ItemEnum::Module(_) => ItemKindData::Module(ModuleMetadata::new()), + ItemEnum::Trait(t) => { + Self::process_trait_items(data, res, path, &t.items)?; + ItemKindData::TraitDef(t.to_cb(data)) + } + ItemEnum::Method(m) => ItemKindData::Method(m.to_cb(data)), + ItemEnum::AssocType { + generics, + bounds, + default, + } => ItemKindData::AssocType(AssocTypeMetadata { + generics: generics.to_cb(data), + bounds: bounds.to_cb(data), + default: default.as_ref().map(|d| d.to_cb(data)), + }), + ItemEnum::AssocConst { type_, default } => { + ItemKindData::AssocConst(AssocConstMetadata { + type_: type_.to_cb(data), + default: default.as_ref().cloned(), + }) + } + ItemEnum::Typedef(t) => ItemKindData::Type(t.to_cb(data)), + _ => return Ok(()), + }; + res.push(( + path.clone(), + ItemKind { + data: kind, + deprecated: item.deprecation.is_some(), + }, + )); + Ok(()) + } - let mut trait_impl_visitor = TraitImplVisitor::new(fn_visitor.items(), &resolver); - trait_impl_visitor.visit_file(program.ast()); + fn find_item<'a>(data: &'a Crate, id: &'a Id) -> AnyResult<&'a Item> { + data.index + .get(id) + .context("Internal error: missing item in summary") + } + + fn process_impls( + data: &Crate, + res: &mut Vec<(ItemPath, ItemKind)>, + path: &ItemPath, + impls: &[Id], + ) -> AnyResult<()> { + for i in impls { + if i.0.starts_with("a:") || i.0.starts_with("b:") { + continue; // auto-implemented or blanket trait + } + let impl_data = Self::find_item(data, i)?; + let impl_inner = match &impl_data.inner { + ItemEnum::Impl(i) => i, + _ => bail!( + "Internal error: unexpected item type: {:?}", + impl_data.inner + ), + }; + if let Some(t) = &impl_inner.trait_ { + let new_path = + path.extend(format!("[impl {}]", t.to_cb(data)), ItemSummaryKind::Impl); + res.push(( + new_path.clone(), + ItemKind { + data: ItemKindData::TraitImpl(impl_inner.to_cb(data)), + deprecated: impl_data.deprecation.is_some(), + }, + )); + for item in &impl_inner.items { + let item_data = Self::find_item(data, item)?; + if !matches!(item_data.inner, rustdoc_types::ItemEnum::Method(_)) { + let new_path = new_path.extend( + item_data.name.clone().unwrap(), + item_data.inner.to_cb(data).in_impl(), + ); + Self::process_item(data, res, &new_path, item_data)?; + } + } + } else { + let new_path = match &impl_inner.for_ { + rustdoc_types::Type::ResolvedPath { + param_names, + args: Some(gen_args), + .. + } if param_names.is_empty() => { + if let rustdoc_types::GenericArgs::AngleBracketed { + ref args, + bindings: _, + } = **gen_args + { + if args.is_empty() { + path.clone() + } else if !args.iter().any(|arg| { + matches!( + arg, + rustdoc_types::GenericArg::Type(rustdoc_types::Type::Generic( + _ + )) + ) + }) { + path.extend( + format!("{}", gen_args.to_cb(data)), + ItemSummaryKind::Impl, + ) + } else { + let p = path.extend( + format!("{}", gen_args.to_cb(data)), + ItemSummaryKind::Impl, + ); + res.push(( + p.clone(), + ItemKind { + data: ItemKindData::TraitImpl(impl_inner.to_cb(data)), + deprecated: impl_data.deprecation.is_some(), + }, + )); + p + } + } else { + bail!( + "Internal error: unexpected generic args for impl target: {:?}", + gen_args + ); + } + } + _ => bail!( + "Internal error: unexpected object for impl target: {:?}", + impl_inner.for_ + ), + }; + for item in &impl_inner.items { + let item_data = Self::find_item(data, item)?; + let new_path = new_path.extend( + item_data.name.clone().unwrap(), + item_data.inner.to_cb(data).in_impl(), + ); + Self::process_item(data, res, &new_path, item_data)?; + } + } + } + Ok(()) + } - let mut trait_def_visitor = TraitDefVisitor::new(trait_impl_visitor.items(), &resolver); - trait_def_visitor.visit_file(program.ast()); + fn process_variants( + data: &Crate, + res: &mut Vec<(ItemPath, ItemKind)>, + path: &ItemPath, + variants: &[Id], + ) -> AnyResult<()> { + for v in variants { + let var_data = Self::find_item(data, v)?; + let name = var_data + .name + .clone() + .context("Internal error: missing variant name")?; + let new_path = path.extend(name.clone(), ItemSummaryKind::Variant); + match &var_data.inner { + ItemEnum::Variant(v) => match v { + Variant::Plain => {} + // tuple struct is equivalent to named struct + // using indices as names + Variant::Tuple(t) => { + for (i, field) in t.iter().enumerate() { + res.push(( + new_path.extend(i.to_string(), ItemSummaryKind::StructField), + ItemKind { + deprecated: false, + data: ItemKindData::Field(TypeFieldMetadata::new( + i.to_string(), + field.to_cb(data), + )), + }, + )); + } + } + Variant::Struct(f) => { + Self::process_fields(data, res, &new_path, f)?; + } + }, + _ => bail!("Unexpected item type in enum: {:?}", var_data.inner), + }; + let value = ItemKind { + data: ItemKindData::Variant(EnumVariantMetadata::new(name.clone())), + deprecated: var_data.deprecation.is_some(), + }; + res.push((new_path, value)); + } + Ok(()) + } - let items = trait_def_visitor.items(); + fn process_fields( + data: &Crate, + res: &mut Vec<(ItemPath, ItemKind)>, + path: &ItemPath, + fields: &[Id], + ) -> AnyResult<()> { + for f in fields { + let field_data = Self::find_item(data, f)?; + let name = field_data + .name + .clone() + .context("Internal error: missing field name")?; + res.push(( + path.extend(name.clone(), ItemSummaryKind::StructField), + ItemKind { + data: ItemKindData::Field(TypeFieldMetadata::new( + name.clone(), + match &field_data.inner { + ItemEnum::StructField(ty) => ty.to_cb(data), + _ => bail!( + "Internal error: Unexpected item type in struct: {:?}", + field_data.inner + ), + }, + )), + deprecated: field_data.deprecation.is_some(), + }, + )); + } + Ok(()) + } - PublicApi { items } + fn process_trait_items( + data: &Crate, + res: &mut Vec<(ItemPath, ItemKind)>, + path: &ItemPath, + items: &[Id], + ) -> AnyResult<()> { + for i in items { + let item_data = Self::find_item(data, i)?; + let name = item_data + .name + .clone() + .context("Internal error: missing trait item name")?; + let new_path = path.extend(name.clone(), item_data.inner.to_cb(data)); + Self::process_item(data, res, &new_path, item_data)?; + } + Ok(()) } pub(crate) fn items(&self) -> &HashMap { @@ -70,31 +328,165 @@ impl PublicApi { } } -impl Parse for PublicApi { - fn parse(input: ParseStream) -> ParseResult { - let ast = input.parse()?; - Ok(PublicApi::from_ast(&ast)) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ItemSummaryKind { + Module, + ExternCrate, + Import, + Struct, + StructField, + Union, + Enum, + Variant, + Function, + Typedef, + OpaqueTy, + Constant, + Trait, + TraitAlias, + Method, + Impl, + Static, + ForeignType, + Macro, + ProcAttribute, + ProcDerive, + AssocConst, + AssocType, + Primitive, + ProcMacro, + PrimitiveType, + Keyword, + Unknown, +} + +impl RustdocToCb for rustdoc_types::ItemKind { + fn to_cb(&self, _data: &Crate) -> ItemSummaryKind { + match self { + rustdoc_types::ItemKind::Module => ItemSummaryKind::Module, + rustdoc_types::ItemKind::ExternCrate => ItemSummaryKind::ExternCrate, + rustdoc_types::ItemKind::Import => ItemSummaryKind::Import, + rustdoc_types::ItemKind::Struct => ItemSummaryKind::Struct, + rustdoc_types::ItemKind::StructField => ItemSummaryKind::StructField, + rustdoc_types::ItemKind::Union => ItemSummaryKind::Union, + rustdoc_types::ItemKind::Enum => ItemSummaryKind::Enum, + rustdoc_types::ItemKind::Variant => ItemSummaryKind::Variant, + rustdoc_types::ItemKind::Function => ItemSummaryKind::Function, + rustdoc_types::ItemKind::Typedef => ItemSummaryKind::Typedef, + rustdoc_types::ItemKind::OpaqueTy => ItemSummaryKind::OpaqueTy, + rustdoc_types::ItemKind::Constant => ItemSummaryKind::Constant, + rustdoc_types::ItemKind::Trait => ItemSummaryKind::Trait, + rustdoc_types::ItemKind::TraitAlias => ItemSummaryKind::TraitAlias, + rustdoc_types::ItemKind::Method => ItemSummaryKind::Method, + rustdoc_types::ItemKind::Impl => ItemSummaryKind::Impl, + rustdoc_types::ItemKind::Static => ItemSummaryKind::Static, + rustdoc_types::ItemKind::ForeignType => ItemSummaryKind::ForeignType, + rustdoc_types::ItemKind::Macro => ItemSummaryKind::Macro, + rustdoc_types::ItemKind::ProcAttribute => ItemSummaryKind::ProcAttribute, + rustdoc_types::ItemKind::ProcDerive => ItemSummaryKind::ProcDerive, + rustdoc_types::ItemKind::AssocConst => ItemSummaryKind::AssocConst, + rustdoc_types::ItemKind::AssocType => ItemSummaryKind::AssocType, + rustdoc_types::ItemKind::Primitive => ItemSummaryKind::Primitive, + rustdoc_types::ItemKind::Keyword => ItemSummaryKind::Keyword, + } + } +} + +impl RustdocToCb for ItemEnum { + fn to_cb(&self, _data: &Crate) -> ItemSummaryKind { + match self { + ItemEnum::Module(_) => ItemSummaryKind::Module, + ItemEnum::ExternCrate { .. } => ItemSummaryKind::ExternCrate, + ItemEnum::Import(_) => ItemSummaryKind::Import, + ItemEnum::Struct(_) => ItemSummaryKind::Struct, + ItemEnum::StructField(_) => ItemSummaryKind::StructField, + ItemEnum::Union(_) => ItemSummaryKind::Union, + ItemEnum::Enum(_) => ItemSummaryKind::Enum, + ItemEnum::Variant(_) => ItemSummaryKind::Variant, + ItemEnum::Function(_) => ItemSummaryKind::Function, + ItemEnum::Typedef(_) => ItemSummaryKind::Typedef, + ItemEnum::OpaqueTy(_) => ItemSummaryKind::OpaqueTy, + ItemEnum::Constant(_) => ItemSummaryKind::Constant, + ItemEnum::Trait(_) => ItemSummaryKind::Trait, + ItemEnum::TraitAlias(_) => ItemSummaryKind::TraitAlias, + ItemEnum::Method(_) => ItemSummaryKind::Method, + ItemEnum::Impl(_) => ItemSummaryKind::Impl, + ItemEnum::Static(_) => ItemSummaryKind::Static, + ItemEnum::ForeignType => ItemSummaryKind::ForeignType, + ItemEnum::Macro(_) => ItemSummaryKind::Macro, + ItemEnum::AssocConst { .. } => ItemSummaryKind::AssocConst, + ItemEnum::AssocType { .. } => ItemSummaryKind::AssocType, + ItemEnum::ProcMacro(_) => ItemSummaryKind::ProcMacro, + ItemEnum::PrimitiveType(_) => ItemSummaryKind::PrimitiveType, + } + } +} + +impl Display for ItemSummaryKind { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{}", + match self { + ItemSummaryKind::Module => "module", + ItemSummaryKind::ExternCrate => "extern crate", + ItemSummaryKind::Import => "import", + ItemSummaryKind::Struct => "struct", + ItemSummaryKind::StructField => "struct field", + ItemSummaryKind::Union => "union", + ItemSummaryKind::Enum => "enum", + ItemSummaryKind::Variant => "variant", + ItemSummaryKind::Function => "function", + ItemSummaryKind::Typedef => "typedef", + ItemSummaryKind::OpaqueTy => "opaque type", + ItemSummaryKind::Constant => "constant", + ItemSummaryKind::Trait => "trait", + ItemSummaryKind::TraitAlias => "trait alias", + ItemSummaryKind::Method => "method", + ItemSummaryKind::Impl => "impl", + ItemSummaryKind::Static => "static", + ItemSummaryKind::ForeignType => "foreign type", + ItemSummaryKind::Macro => "macro", + ItemSummaryKind::ProcAttribute => "proc attribute", + ItemSummaryKind::ProcDerive => "proc derive", + ItemSummaryKind::AssocConst => "associated constant", + ItemSummaryKind::AssocType => "associated type", + ItemSummaryKind::ProcMacro => "proc macro", + ItemSummaryKind::Primitive => "primitive", + ItemSummaryKind::PrimitiveType => "primitive type", + ItemSummaryKind::Keyword => "keyword", + ItemSummaryKind::Unknown => + "unknown: this should not happen; please file a bug report", + } + ) + } +} + +impl ItemSummaryKind { + pub fn in_impl(self) -> ItemSummaryKind { + match self { + ItemSummaryKind::Typedef => ItemSummaryKind::AssocType, + _ => self, + } } } #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub(crate) struct ItemPath { - path: Vec, + pub path: Vec, + pub kind: ItemSummaryKind, } impl ItemPath { - fn new(mut path: Vec, last: Ident) -> ItemPath { - path.push(last); - ItemPath { path } - } - - fn concat_both(left: Vec, right: Vec) -> ItemPath { - let path = left.tap_mut(|v| v.extend(right)); - ItemPath { path } + pub(crate) fn new(path: Vec, kind: ItemSummaryKind) -> ItemPath { + ItemPath { path, kind } } - fn extend(initial: ItemPath, last: Ident) -> ItemPath { - initial.tap_mut(|initial| initial.path.push(last)) + pub(crate) fn extend(&self, last: String, kind: ItemSummaryKind) -> ItemPath { + self.clone().tap_mut(|path| { + path.path.push(last); + path.kind = kind; + }) } } @@ -107,65 +499,73 @@ impl Display for ItemPath { .iter() .skip(1) .try_for_each(|segment| write!(f, "::{}", segment))?; - } - Ok(()) + write!(f, " ({})", self.kind) + } else { + unreachable!("empty path") + } } } -#[cfg(test)] -impl Parse for ItemPath { - fn parse(input: ParseStream) -> ParseResult { - let first_ident = input.parse::()?; +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct ItemKind { + pub data: ItemKindData, + pub deprecated: bool, +} - let mut path = vec![first_ident]; +impl DiagnosticGenerator for ItemKind { + fn removal_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { + self.data.removal_diagnosis(path, collector); + } - while input.peek(Token![::]) { - input.parse::().unwrap(); - path.push(input.parse()?); + fn modification_diagnosis( + &self, + other: &Self, + path: &ItemPath, + collector: &mut DiagnosisCollector, + ) { + if !self.deprecated && other.deprecated { + collector.add(DiagnosisItem::deprecation(path.clone())); } + if self.data != other.data { + self.data + .modification_diagnosis(&other.data, path, collector); + } + } - let last_segment = path.pop().unwrap(); - Ok(ItemPath::new(path, last_segment)) + fn addition_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { + self.data.addition_diagnosis(path, collector); } } #[derive(Clone, Debug, PartialEq)] -pub(crate) enum ItemKind { +pub(crate) enum ItemKindData { Fn(FnPrototype), Type(TypeMetadata), Method(MethodMetadata), + Module(ModuleMetadata), + Field(TypeFieldMetadata), + Variant(EnumVariantMetadata), TraitDef(TraitDefMetadata), + TraitImpl(TraitImplMetadata), + AssocType(AssocTypeMetadata), + AssocConst(AssocConstMetadata), } -impl ItemKind { - pub(crate) fn as_type_mut(&mut self) -> Option<&mut TypeMetadata> { - if let Self::Type(v) = self { - Some(v) - } else { - None - } - } -} - -#[cfg(test)] -impl ItemKind { - fn as_type(&self) -> Option<&TypeMetadata> { - if let ItemKind::Type(v) = self { - Some(v) - } else { - None - } - } -} - -impl DiagnosticGenerator for ItemKind { +impl DiagnosticGenerator for ItemKindData { fn removal_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { match self { - ItemKind::Fn(f) => f.removal_diagnosis(path, collector), - ItemKind::Type(t) => t.removal_diagnosis(path, collector), - ItemKind::Method(m) => m.removal_diagnosis(path, collector), - ItemKind::TraitDef(t) => t.removal_diagnosis(path, collector), + ItemKindData::Fn(f) => f.removal_diagnosis(path, collector), + ItemKindData::Type(t) => t.removal_diagnosis(path, collector), + ItemKindData::Method(m) => m.removal_diagnosis(path, collector), + ItemKindData::Module(m) => m.removal_diagnosis(path, collector), + ItemKindData::Field(f) => f.removal_diagnosis(path, collector), + ItemKindData::Variant(v) => v.removal_diagnosis(path, collector), + ItemKindData::TraitDef(t) => t.removal_diagnosis(path, collector), + ItemKindData::TraitImpl(t) => t.removal_diagnosis(path, collector), + ItemKindData::AssocType(a) => a.removal_diagnosis(path, collector), + ItemKindData::AssocConst(a) => a.removal_diagnosis(path, collector), + //temKind::TraitDef(t) => t.removal_diagnosis(path, collector), } } @@ -176,16 +576,39 @@ impl DiagnosticGenerator for ItemKind { collector: &mut DiagnosisCollector, ) { match (self, other) { - (ItemKind::Fn(fa), ItemKind::Fn(fb)) => fa.modification_diagnosis(fb, path, collector), - (ItemKind::Type(ta), ItemKind::Type(tb)) => { + (ItemKindData::Fn(fa), ItemKindData::Fn(fb)) => { + fa.modification_diagnosis(fb, path, collector) + } + (ItemKindData::Type(ta), ItemKindData::Type(tb)) => { ta.modification_diagnosis(tb, path, collector) } - (ItemKind::Method(ma), ItemKind::Method(mb)) => { + (ItemKindData::Method(ma), ItemKindData::Method(mb)) => { ma.modification_diagnosis(mb, path, collector) } - (ItemKind::TraitDef(ta), ItemKind::TraitDef(tb)) => { + (ItemKindData::Module(ma), ItemKindData::Module(mb)) => { + ma.modification_diagnosis(mb, path, collector) + } + (ItemKindData::Field(fa), ItemKindData::Field(fb)) => { + fa.modification_diagnosis(fb, path, collector) + } + (ItemKindData::Variant(va), ItemKindData::Variant(vb)) => { + va.modification_diagnosis(vb, path, collector) + } + (ItemKindData::TraitDef(ta), ItemKindData::TraitDef(tb)) => { ta.modification_diagnosis(tb, path, collector) } + (ItemKindData::TraitImpl(ta), ItemKindData::TraitImpl(tb)) => { + ta.modification_diagnosis(tb, path, collector) + } + (ItemKindData::AssocType(ta), ItemKindData::AssocType(tb)) => { + ta.modification_diagnosis(tb, path, collector) + } + (ItemKindData::AssocConst(ta), ItemKindData::AssocConst(tb)) => { + ta.modification_diagnosis(tb, path, collector) + } + /*(ItemKind::TraitDef(ta), ItemKind::TraitDef(tb)) => { + ta.modification_diagnosis(tb, path, collector) + }*/ (a, b) => { a.removal_diagnosis(path, collector); b.addition_diagnosis(path, collector); @@ -195,324 +618,17 @@ impl DiagnosticGenerator for ItemKind { fn addition_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { match self { - ItemKind::Fn(f) => f.addition_diagnosis(path, collector), - ItemKind::Type(t) => t.addition_diagnosis(path, collector), - ItemKind::Method(m) => m.addition_diagnosis(path, collector), - ItemKind::TraitDef(t) => t.addition_diagnosis(path, collector), - } - } -} - -#[cfg(test)] -impl Parse for ItemKind { - fn parse(input: ParseStream) -> ParseResult { - input - .parse::() - .map(Into::into) - .or_else(|mut e| { - input.parse::().map(Into::into).map_err(|e_| { - e.combine(e_); - e - }) - }) - .or_else(|mut e| { - input - .parse::() - .map(Into::into) - .map_err(|e_| { - e.combine(e_); - e - }) - }) - .or_else(|mut e| { - input - .parse::() - .map(Into::into) - .map_err(|e_| { - e.combine(e_); - e - }) - }) - } -} - -impl From for ItemKind { - fn from(item: FnPrototype) -> Self { - ItemKind::Fn(item) - } -} - -impl From for ItemKind { - fn from(item: TypeMetadata) -> Self { - ItemKind::Type(item) - } -} - -impl From for ItemKind { - fn from(v: MethodMetadata) -> ItemKind { - ItemKind::Method(v) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod public_api { - use syn::parse_quote; - - use crate::public_api::trait_impls::TraitImplMetadata; - - use super::*; - - #[test] - fn adds_functions() { - let public_api: PublicApi = parse_quote! { - pub fn fact(n: u32) -> u32 {} - }; - - assert_eq!(public_api.items.len(), 1); - - let item_kind = parse_quote! { - pub fn fact(n: u32) -> u32 - }; - - let k = parse_quote! { fact }; - let left = public_api.items.get(&k); - let right = Some(&item_kind); - - assert_eq!(left, right); - } - - #[test] - fn adds_structure() { - let public_api: PublicApi = parse_quote! { pub struct A; }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { struct A; }; - - let k = parse_quote! { A }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn adds_enum() { - let public_api: PublicApi = parse_quote! { pub enum B {} }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { enum B {} }; - - let k = parse_quote! { B }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn filters_private_named_struct_fields() { - let public_api: PublicApi = parse_quote! { pub struct A { a: u8, pub b: u8 } }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { - pub struct A { - pub b: u8 - } - }; - - let k = parse_quote! { A }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn filters_private_unnamed_struct_fields() { - let public_api: PublicApi = parse_quote! { pub struct A(u8, pub u8); }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { pub struct A(pub u8); }; - - let k = parse_quote! { A }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn filters_named_enum_variant() { - let public_api: PublicApi = parse_quote! { - pub enum A { - A { - a: u8, - pub b: u16, - }, - } - }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { - pub enum A { - A { - pub b: u16 - }, - } - }; - - let k = parse_quote! { A }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn filters_unnamed_enum_variant() { - let public_api: PublicApi = parse_quote! { - pub enum A { - A(u8, pub u8), - } - }; - - assert_eq!(public_api.items.len(), 1); - - let item = parse_quote! { - pub enum A { - A(pub u8), - } - }; - - let k = parse_quote! { A }; - let left = public_api.items.get(&k); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - #[should_panic(expected = "Duplicate item definition")] - fn panics_on_redefinition_1() { - let _: PublicApi = parse_quote! { - pub fn a () {} - pub fn a() {} - }; - } - - #[test] - #[should_panic(expected = "Duplicate item definition")] - fn panics_on_redefinition_2() { - let _: PublicApi = parse_quote! { - pub struct A; - pub struct A; - }; - } - - #[test] - fn adds_associated_function() { - let public_api: PublicApi = parse_quote! { - pub struct A; - - impl A { - pub fn a() {} - } - }; - - assert_eq!(public_api.items.len(), 2); - - let struct_key = parse_quote! { A }; - assert!(public_api.items.get(&struct_key).is_some()); - - let item = parse_quote! { - impl A { - fn a() {} - } - }; - - let fn_key = parse_quote! { A::a }; - let left = public_api.items.get(&fn_key); - let right = Some(&item); - - assert_eq!(left, right); - } - - #[test] - fn adds_trait_implementation() { - let public_api: PublicApi = parse_quote! { - pub struct S; - impl T for S {} - }; - - assert_eq!(public_api.items.len(), 1); - - let type_key = parse_quote! { S }; - let type_value = public_api.items.get(&type_key).unwrap(); - - let trait_metadata: TraitImplMetadata = parse_quote! { - impl T for S {} - pub struct S; - }; - - let left = &[trait_metadata]; - let right = type_value.as_type().unwrap().traits(); - - assert_eq!(left, right); - } - - #[test] - fn adds_trait_definition() { - let public_api: PublicApi = parse_quote! { - pub trait T {} - }; - - assert_eq!(public_api.items.len(), 1); - - let trait_key = parse_quote! { T }; - let left = public_api.items.get(&trait_key).unwrap(); - - let right = parse_quote! { - pub trait T {} - }; - - assert_eq!(left, &right); - } - - #[test] - fn filters_non_public_trait_definition() { - let public_api: PublicApi = parse_quote! { - trait T {} - }; - - assert!(public_api.items.is_empty()); - } - - #[test] - fn filters_public_test_in_non_public_module() { - let public_api: PublicApi = parse_quote! { - mod m { - pub trait T {} - } - }; - - assert!(public_api.items.is_empty()); - } - - #[test] - #[should_panic(expected = "Duplicate item definition")] - fn panics_on_redefinition_3() { - let _: PublicApi = parse_quote! { - pub trait T {} - pub trait T {} - }; + ItemKindData::Fn(f) => f.addition_diagnosis(path, collector), + ItemKindData::Type(t) => t.addition_diagnosis(path, collector), + ItemKindData::Method(m) => m.addition_diagnosis(path, collector), + ItemKindData::Module(m) => m.addition_diagnosis(path, collector), + ItemKindData::Field(f) => f.addition_diagnosis(path, collector), + ItemKindData::Variant(v) => v.addition_diagnosis(path, collector), + ItemKindData::TraitDef(t) => t.addition_diagnosis(path, collector), + ItemKindData::TraitImpl(t) => t.addition_diagnosis(path, collector), + ItemKindData::AssocType(a) => a.addition_diagnosis(path, collector), + ItemKindData::AssocConst(a) => a.addition_diagnosis(path, collector), + //ItemKind::TraitDef(t) => t.addition_diagnosis(path, collector), } } } diff --git a/src/public_api/assoc_const.rs b/src/public_api/assoc_const.rs new file mode 100644 index 0000000..b4e1c17 --- /dev/null +++ b/src/public_api/assoc_const.rs @@ -0,0 +1,10 @@ +use crate::diagnosis::DiagnosticGenerator; +use crate::rustdoc::types::Type; + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct AssocConstMetadata { + pub type_: Type, + pub default: Option, +} + +impl DiagnosticGenerator for AssocConstMetadata {} diff --git a/src/public_api/assoc_type.rs b/src/public_api/assoc_type.rs new file mode 100644 index 0000000..cb82896 --- /dev/null +++ b/src/public_api/assoc_type.rs @@ -0,0 +1,11 @@ +use crate::diagnosis::DiagnosticGenerator; +use crate::rustdoc::types::{GenericBound, Generics, Type}; + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct AssocTypeMetadata { + pub generics: Generics, + pub bounds: Vec, + pub default: Option, +} + +impl DiagnosticGenerator for AssocTypeMetadata {} diff --git a/src/public_api/functions.rs b/src/public_api/functions.rs index 64a91d9..b7937ee 100644 --- a/src/public_api/functions.rs +++ b/src/public_api/functions.rs @@ -1,98 +1,24 @@ -use std::collections::HashMap; - -use syn::{ - visit::{self, Visit}, - Ident, ItemFn, ItemMod, Signature, Visibility, -}; - -#[cfg(test)] -use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}; +use rustdoc_types::{Crate, Header}; use crate::diagnosis::DiagnosticGenerator; -use super::{ItemKind, ItemPath}; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct FnVisitor { - items: HashMap, - path: Vec, -} - -impl FnVisitor { - pub(crate) fn new(items: HashMap) -> FnVisitor { - let path = Vec::new(); - - FnVisitor { items, path } - } - - pub(crate) fn items(self) -> HashMap { - self.items - } - - fn add_path_segment(&mut self, segment: Ident) { - self.path.push(segment); - } - - fn remove_path_segment(&mut self) { - self.path.pop().unwrap(); - } - - fn add_fn(&mut self, path: ItemPath, fn_: FnPrototype) { - let tmp = self.items.insert(path, fn_.into()); - - assert!(tmp.is_none(), "Duplicate item definition"); - } -} - -impl<'ast> Visit<'ast> for FnVisitor { - fn visit_item_mod(&mut self, mod_: &'ast ItemMod) { - self.add_path_segment(mod_.ident.clone()); - visit::visit_item_mod(self, mod_); - self.remove_path_segment(); - } - - fn visit_item_fn(&mut self, fn_: &'ast ItemFn) { - if !matches!(fn_.vis, Visibility::Public(_)) { - return; - } - - let path = ItemPath::new(self.path.clone(), fn_.sig.ident.clone()); - let fn_ = FnPrototype::new(fn_.sig.clone()); - - self.add_fn(path, fn_); - } -} +use crate::rustdoc::types::{FnDecl, Generics, RustdocToCb}; #[derive(Clone, Debug, PartialEq)] pub(crate) struct FnPrototype { - sig: Signature, -} - -impl FnPrototype { - fn new(mut sig: Signature) -> FnPrototype { - if let Some(last) = sig.inputs.pop() { - sig.inputs.push(last.value().clone()); - } - FnPrototype { sig } - } + pub decl: FnDecl, + pub generics: Generics, + pub header: Header, } impl DiagnosticGenerator for FnPrototype {} -#[cfg(test)] -impl Parse for FnPrototype { - fn parse(input: ParseStream) -> ParseResult { - let vis = input.parse()?; - - if !matches!(vis, Visibility::Public(_)) { - let err_span = input.span(); - return Err(ParseError::new( - err_span, - "Found non-public function in test code", - )); +impl RustdocToCb for rustdoc_types::Function { + fn to_cb(&self, data: &Crate) -> FnPrototype { + FnPrototype { + decl: self.decl.to_cb(data), + generics: self.generics.to_cb(data), + header: self.header.clone(), } - - let sig = input.parse()?; - Ok(FnPrototype { sig }) } } diff --git a/src/public_api/imports.rs b/src/public_api/imports.rs deleted file mode 100644 index 602f173..0000000 --- a/src/public_api/imports.rs +++ /dev/null @@ -1,549 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - iter::{self, Peekable}, -}; - -use syn::{ - parse_quote, - visit::{self, Visit}, - Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, ItemUse, Path, UseTree, Visibility, -}; - -#[cfg(test)] -use syn::parse::{Parse, ParseStream, Result as ParseResult}; -use tap::Tap; - -use crate::ast::CrateAst; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct PathResolver { - // Note: we store only public items. - items: HashSet>, - uses: HashMap, Vec<(Import, UseVisibility)>>, -} - -impl PathResolver { - pub(crate) fn new(ast: &CrateAst) -> PathResolver { - let mut resolver = PathResolver { - items: HashSet::new(), - uses: HashMap::new(), - }; - - let mut visitor = ExportedItemsVisitor::new(&mut resolver); - - visitor.visit_file(ast.ast()); - - resolver - } - - pub(crate) fn resolve(&self, current_path: &[Ident], item_path: &Path) -> Option<&[Ident]> { - let mut item_idents = item_path - .segments - .iter() - .map(|segment| &segment.ident) - .peekable(); - - let from_current_path = self - .find_rooted_path(&mut item_idents) - .or_else(|| self.find_import_for_path(current_path, &mut item_idents)) - .unwrap_or(current_path); - - let full_path_iter = from_current_path.iter().chain(item_idents); - - // The provided capacity is not always correct, but it is always a - // correct upper bound. - let mut full_path = Vec::with_capacity(from_current_path.len() + item_path.segments.len()); - - for item in full_path_iter { - if item == "super" { - full_path.pop().unwrap(); - } else { - full_path.push(item.clone()); - } - } - - self.items.get(full_path.as_slice()).map(Vec::as_slice) - } - - // Note: item_path is taken by mutable reference because it is expected to - // discard the path segment if we have a match. - fn find_rooted_path<'a>( - &self, - item_path: &mut Peekable>, - ) -> Option<&[Ident]> { - item_path.next_if_eq(&"crate").map(|_| &[] as _) - } - - // Note: item_path is taken by mutable reference because it is expected to - // discard the path segment if we have a match. - fn find_import_for_path<'a>( - &self, - current_path: &[Ident], - item_path: &mut Peekable>, - ) -> Option<&[Ident]> { - let imports_in_module = self.uses.get(current_path)?; - - imports_in_module - .iter() - .find_map(|(import, _)| item_path.next_if_eq(&import.name()).map(|_| import.path())) - } -} - -#[cfg(test)] -impl Parse for PathResolver { - fn parse(input: ParseStream) -> ParseResult { - Ok(PathResolver::new(&input.parse()?)) - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum UseVisibility { - Private, - PubCrate, - Pub, -} - -struct ExportedItemsVisitor<'a> { - items: &'a mut HashSet>, - uses: &'a mut HashMap, Vec<(Import, UseVisibility)>>, - path: Vec, -} - -impl<'a> ExportedItemsVisitor<'a> { - fn new(resolver: &'a mut PathResolver) -> ExportedItemsVisitor<'a> { - ExportedItemsVisitor { - items: &mut resolver.items, - uses: &mut resolver.uses, - path: Vec::new(), - } - } - - fn add_path_segment(&mut self, segment: Ident) { - self.path.push(segment); - } - - fn remove_path_segment(&mut self) { - self.path.pop().unwrap(); - } - - fn create_full_path(&self, item_ident: Ident) -> Vec { - self.path.clone().tap_mut(|p| p.push(item_ident)) - } - - fn add_import(&mut self, path: Vec, import: Import, vis: UseVisibility) { - let uses_at_path = self.uses.entry(path).or_default(); - - uses_at_path.push((import, vis)) - } -} - -impl<'a, 'ast> Visit<'ast> for ExportedItemsVisitor<'ast> { - fn visit_item_mod(&mut self, i: &'ast ItemMod) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let module_path = self.create_full_path(i.ident.clone()); - self.items.insert(module_path); - - self.add_path_segment(i.ident.clone()); - visit::visit_item_mod(self, i); - self.remove_path_segment(); - } - - fn visit_item_fn(&mut self, i: &'ast ItemFn) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let fn_path = self.create_full_path(i.sig.ident.clone()); - self.items.insert(fn_path); - } - - fn visit_item_struct(&mut self, i: &'ast ItemStruct) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let struct_path = self.create_full_path(i.ident.clone()); - self.items.insert(struct_path); - } - - fn visit_item_enum(&mut self, i: &'ast ItemEnum) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let enum_path = self.create_full_path(i.ident.clone()); - self.items.insert(enum_path); - } - - fn visit_item_use(&mut self, i: &'ast ItemUse) { - let vis = match &i.vis { - Visibility::Inherited => UseVisibility::Private, - Visibility::Crate(_) => UseVisibility::PubCrate, - Visibility::Public(_) => UseVisibility::Pub, - _ => todo!(), - }; - - for imported_item in flatten_use_tree(&i.tree) { - self.add_import(self.path.to_owned(), imported_item, vis) - } - } -} - -fn flatten_use_tree(tree: &UseTree) -> Vec { - fn flatten_use_tree_inner(tree: &UseTree, current: &[Ident]) -> Vec { - match tree { - UseTree::Path(p) if p.ident == "super" => flatten_use_tree_inner(&p.tree, current), - - UseTree::Path(p) => { - let current = current - .iter() - .chain(iter::once(&p.ident)) - .cloned() - .collect::>(); - - flatten_use_tree_inner(&p.tree, current.as_slice()) - } - - UseTree::Name(n) => { - let current = current - .iter() - .cloned() - .chain(iter::once(n.ident.clone())) - .collect::>(); - - vec![Import::non_renamed(current)] - } - - UseTree::Group(g) => g - .items - .iter() - .flat_map(|item| flatten_use_tree_inner(item, current)) - .collect(), - - UseTree::Rename(r) => { - let name = r.rename.clone(); - let path = current - .iter() - .chain(iter::once(&r.ident)) - .cloned() - .collect(); - - vec![Import::renamed(path, name)] - } - - _ if current.starts_with(&[parse_quote! { std }, parse_quote! { prelude }]) => { - Vec::new() - } - - _ => todo!(), - } - } - - flatten_use_tree_inner(tree, &[]) -} - -#[derive(Clone, Debug, Default, PartialEq)] -struct Import { - path: Vec, - renamed: Option, -} - -impl Import { - fn non_renamed(path: Vec) -> Import { - Import { - path, - ..Default::default() - } - } - - fn renamed(path: Vec, name: Ident) -> Import { - let renamed = Some(name); - Import { path, renamed } - } - - fn path(&self) -> &[Ident] { - self.path.as_slice() - } - - fn name(&self) -> &Ident { - self.renamed - .as_ref() - .unwrap_or_else(|| self.path.last().unwrap()) - } -} - -#[cfg(test)] -mod tests { - use syn::parse_quote; - - use super::*; - - #[test] - fn adds_function_on_root_1() { - let resolver: PathResolver = parse_quote! { - pub fn a() {} - }; - - let path = []; - let item_to_resolve = parse_quote! { a }; - - let tmp = [parse_quote! { a }]; - - let left = resolver.resolve(&path, &item_to_resolve); - let right = Some(&tmp as &[_]); - - assert_eq!(left, right); - } - - #[test] - fn adds_function_on_root_2() { - let resolver: PathResolver = parse_quote! { - pub mod a { - pub fn f() {} - } - }; - - let tmp = [parse_quote! { a }, parse_quote! { f }]; - - let left = resolver.resolve(&[], &parse_quote! { a::f }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn adds_function_in_public_module() { - let resolver: PathResolver = parse_quote! { - pub mod a { - pub fn b() {} - } - }; - - let path = [parse_quote! { a }]; - let item_to_resolve = parse_quote! { b }; - - let tmp = [parse_quote! { a }, parse_quote! { b }]; - - let left = resolver.resolve(&path, &item_to_resolve); - let right = Some(&tmp as &[_]); - - assert_eq!(left, right); - } - - #[test] - fn adds_public_module() { - let resolver: PathResolver = parse_quote! { - pub mod a {} - }; - - let path = []; - let item_to_resolve = parse_quote! { a }; - - let tmp = [parse_quote! { a }]; - - let left = resolver.resolve(&path, &item_to_resolve); - let right = Some(&tmp as &[_]); - - assert_eq!(left, right); - } - - #[test] - fn adds_struct() { - let resolver: PathResolver = parse_quote! { - pub struct S; - }; - - let tmp = [parse_quote! { S }]; - - let left = resolver.resolve(&[], &parse_quote! { S }); - let right = Some(&tmp as &[_]); - - assert_eq!(left, right); - } - - #[test] - fn adds_enum() { - let resolver: PathResolver = parse_quote! { - pub enum E {} - }; - - let tmp = [parse_quote! { E }]; - - let left = resolver.resolve(&[], &parse_quote! { E }); - let right = Some(&tmp as &[_]); - - assert_eq!(left, right); - } - - #[test] - fn resolves_when_starts_with_crate() { - let resolver: PathResolver = parse_quote! { - pub mod foo { - pub fn bar() {} - } - }; - - let tmp = [parse_quote! { foo }, parse_quote! { bar }]; - - let left = resolver.resolve(&[], &parse_quote! { crate::foo::bar }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn resolves_when_brought_in_by_use_single_segment() { - let resolver: PathResolver = parse_quote! { - use foo::bar; - - pub mod foo { - pub fn bar() {} - } - }; - - let tmp = [parse_quote! { foo }, parse_quote! { bar }]; - - let left = resolver.resolve(&[], &parse_quote! { bar }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn resolves_when_brought_in_by_grouped_import_ident() { - let resolver: PathResolver = parse_quote! { - use foo::{bar}; - - pub mod foo { - pub fn bar() {} - } - }; - - let tmp = [parse_quote! { foo }, parse_quote! { bar }]; - - let left = resolver.resolve(&[], &parse_quote! { bar }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn resolves_when_brought_in_by_grouped_import_subpath() { - let resolver: PathResolver = parse_quote! { - use {foo::bar}; - - pub mod foo { - pub fn bar() {} - } - }; - - let tmp = [parse_quote! { foo }, parse_quote! { bar }]; - - let left = resolver.resolve(&[], &parse_quote! { bar }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn handles_renaming() { - let resolver: PathResolver = parse_quote! { - use foo::bar as baz; - - pub mod foo { - pub fn bar() {} - } - }; - - let tmp = [parse_quote! { foo }, parse_quote! { bar }]; - - let left = resolver.resolve(&[], &parse_quote! { baz }); - let right = Some(&tmp as _); - - assert_eq!(left, right); - } - - #[test] - fn handles_super_on_resolution() { - let resolver: PathResolver = parse_quote! { - mod foo {} - - pub struct Foo; - }; - - let tmp = [parse_quote! { Foo }]; - - let left = resolver.resolve(&[parse_quote! { foo }], &parse_quote! { super::Foo }); - let right = Some(&tmp as &_); - - assert_eq!(left, right); - } - - #[test] - fn handles_super_on_import() { - let resolver: PathResolver = parse_quote! { - pub mod foo { - use super::Foo; - } - - pub struct Foo; - }; - - let tmp = [parse_quote! { Foo }]; - - let left = resolver.resolve(&[parse_quote! { foo }], &parse_quote! { Foo }); - let right = Some(&tmp as &_); - - assert_eq!(left, right); - } - - #[test] - fn handles_two_supers_on_import() { - let resolver: PathResolver = parse_quote! { - pub mod foo { - pub mod bar { - use super::super::Foo; - } - } - - pub struct Foo; - }; - - let tmp = [parse_quote! { Foo }]; - - let left = resolver.resolve( - &[parse_quote! { foo }, parse_quote! { bar }], - &parse_quote! { Foo }, - ); - let right = Some(&tmp as &_); - - assert_eq!(left, right); - } - - // Yes I do want to name my variable foo or baz. - #[allow(clippy::blacklisted_name)] - #[test] - fn handles_super_anywhere_in_path() { - let resolver: PathResolver = parse_quote! { - pub mod foo { - pub mod bar { - pub struct Baz; - } - } - }; - - let foo: Ident = parse_quote! { foo }; - let bar: Ident = parse_quote! { bar }; - let baz: Ident = parse_quote! { Baz }; - - let tmp = [foo.clone(), bar, baz]; - - let left = resolver.resolve(&[foo], &parse_quote! { bar::super::bar::Baz }); - let right = Some(&tmp as &_); - - assert_eq!(left, right); - } -} diff --git a/src/public_api/methods.rs b/src/public_api/methods.rs index a2bb2b5..81cb55d 100644 --- a/src/public_api/methods.rs +++ b/src/public_api/methods.rs @@ -1,178 +1,26 @@ -use std::collections::HashMap; - -use syn::{ - visit::{self, Visit}, - AngleBracketedGenericArguments, Generics, Ident, ImplItemMethod, ItemImpl, ItemMod, Signature, - Visibility, -}; - -#[cfg(test)] -use syn::{ - parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}, - spanned::Spanned, -}; +use rustdoc_types::{Crate, Header}; use crate::diagnosis::DiagnosticGenerator; -use super::{imports::PathResolver, utils, ItemKind, ItemPath}; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct MethodVisitor<'a> { - items: HashMap, - path: Vec, - resolver: &'a PathResolver, -} - -impl<'a> MethodVisitor<'a> { - pub(crate) fn new( - types: HashMap, - resolver: &'a PathResolver, - ) -> MethodVisitor<'a> { - let items = types; - let path = Vec::new(); - - MethodVisitor { - items, - path, - resolver, - } - } - - pub(crate) fn items(self) -> HashMap { - self.items - } - - fn add_path_segment(&mut self, segment: Ident) { - self.path.push(segment); - } - - fn remove_path_segment(&mut self) { - self.path.pop().unwrap(); - } -} - -impl<'a, 'ast> Visit<'ast> for MethodVisitor<'a> { - fn visit_item_mod(&mut self, mod_: &'ast ItemMod) { - self.add_path_segment(mod_.ident.clone()); - visit::visit_item_mod(self, mod_); - self.remove_path_segment(); - } - - fn visit_item_impl(&mut self, impl_: &'ast ItemImpl) { - if impl_.trait_.is_some() { - return; - } - - let (type_path, generic_args) = - match utils::extract_name_and_generic_args(impl_.self_ty.as_ref()) { - Some((name, generic_args)) => (name, generic_args.cloned()), - // TODO: handle non-trivial paths - None => return, - }; - - let resolved_type_path = match self.resolver.resolve(self.path.as_slice(), type_path) { - Some(resolved) => resolved, - None => return, - }; - - let generic_params = &impl_.generics; - - let mut impl_block_visitor = ImplBlockVisitor { - items: &mut self.items, - path: resolved_type_path, - parent_generic_params: generic_params, - parent_generic_args: &generic_args, - }; - impl_block_visitor.visit_item_impl(impl_); - } -} - -#[derive(Debug, PartialEq)] -struct ImplBlockVisitor<'a> { - items: &'a mut HashMap, - path: &'a [Ident], - parent_generic_params: &'a Generics, - parent_generic_args: &'a Option, -} - -impl<'a> ImplBlockVisitor<'a> { - fn add_method(&mut self, path: ItemPath, method: MethodMetadata) { - let tmp = self.items.insert(path, method.into()); - - assert!(tmp.is_none(), "Duplicate item definition"); - } -} - -impl<'a, 'ast> Visit<'ast> for ImplBlockVisitor<'a> { - fn visit_impl_item_method(&mut self, method: &'ast ImplItemMethod) { - if !matches!(method.vis, Visibility::Public(_)) { - return; - } - - let path = ItemPath::new(self.path.to_owned(), method.sig.ident.clone()); - let method = MethodMetadata::new( - method.sig.clone(), - self.parent_generic_params.clone(), - self.parent_generic_args.clone(), - ); - - self.add_method(path, method); - } -} +use crate::rustdoc::types::{FnDecl, Generics, RustdocToCb}; #[derive(Clone, Debug, PartialEq)] pub(crate) struct MethodMetadata { - signature: Signature, - parent_generic_params: Generics, - parent_generic_args: Option, -} - -impl MethodMetadata { - fn new( - signature: Signature, - parent_generic_params: Generics, - parent_generic_args: Option, - ) -> MethodMetadata { - MethodMetadata { - signature, - parent_generic_params, - parent_generic_args, - } - } + pub decl: FnDecl, + pub generics: Generics, + pub header: Header, + pub has_body: bool, } impl DiagnosticGenerator for MethodMetadata {} -#[cfg(test)] -impl Parse for MethodMetadata { - fn parse(input: ParseStream) -> ParseResult { - let impl_block = input.parse::()?; - - let parent_generc_params = &impl_block.generics; - let (_, parent_generic_arguments) = - utils::extract_name_and_generic_args(&impl_block.self_ty).unwrap(); - - let inner_item = match impl_block.items.len() { - 1 => impl_block.items.last().unwrap(), - _ => { - return Err(ParseError::new( - impl_block.span(), - "Excepted a single function", - )) - } - }; - - let method = match inner_item { - syn::ImplItem::Method(m) => m, - _ => return Err(ParseError::new(inner_item.span(), "Excepted a method")), - }; - - let sig = &method.sig; - - Ok(MethodMetadata::new( - sig.clone(), - parent_generc_params.clone(), - parent_generic_arguments.cloned(), - )) +impl RustdocToCb for rustdoc_types::Method { + fn to_cb(&self, data: &Crate) -> MethodMetadata { + MethodMetadata { + decl: self.decl.to_cb(data), + generics: self.generics.to_cb(data), + header: self.header.clone(), + has_body: self.has_body, + } } } diff --git a/src/public_api/modules.rs b/src/public_api/modules.rs new file mode 100644 index 0000000..0153a6f --- /dev/null +++ b/src/public_api/modules.rs @@ -0,0 +1,12 @@ +use crate::diagnosis::DiagnosticGenerator; + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct ModuleMetadata {} + +impl ModuleMetadata { + pub fn new() -> ModuleMetadata { + ModuleMetadata {} + } +} + +impl DiagnosticGenerator for ModuleMetadata {} diff --git a/src/public_api/trait_defs.rs b/src/public_api/trait_defs.rs index 1f5c228..7c13cbb 100644 --- a/src/public_api/trait_defs.rs +++ b/src/public_api/trait_defs.rs @@ -1,225 +1,24 @@ -use std::collections::HashMap; - -use syn::{ - punctuated::Punctuated, - token::Add, - visit::{self, Visit}, - Generics, Ident, ItemMod, ItemTrait, TraitItem, TraitItemConst, TraitItemMethod, TraitItemType, - TypeParamBound, Visibility, -}; - -#[cfg(test)] -use syn::parse::{Parse, ParseStream, Result as ParseResult}; - -use crate::diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}; - -use super::{imports::PathResolver, ItemKind, ItemPath}; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct TraitDefVisitor<'a> { - items: HashMap, - path: Vec, - resolver: &'a PathResolver, -} - -impl<'a> TraitDefVisitor<'a> { - pub(crate) fn new( - items: HashMap, - resolver: &'a PathResolver, - ) -> TraitDefVisitor<'a> { - let path = Vec::new(); - TraitDefVisitor { - items, - path, - resolver, - } - } - - pub(crate) fn items(self) -> HashMap { - self.items - } - - pub(crate) fn add_trait_def(&mut self, path: ItemPath, metadata: TraitDefMetadata) { - let tmp = self.items.insert(path, metadata.into()); - assert!(tmp.is_none(), "Duplicate item definition"); - } -} - -impl<'a, 'ast> Visit<'ast> for TraitDefVisitor<'a> { - fn visit_item_trait(&mut self, i: &'ast ItemTrait) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let path = ItemPath::new(self.path.clone(), i.ident.clone()); - let metadata = extract_def_trait_metadata(i); - - self.add_trait_def(path, metadata); - } - - fn visit_item_mod(&mut self, i: &'ast ItemMod) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - self.path.push(i.ident.clone()); - visit::visit_item_mod(self, i); - self.path.pop().unwrap(); - } -} - -fn extract_def_trait_metadata(i: &ItemTrait) -> TraitDefMetadata { - let generics = i.generics.clone(); - let supertraits = i.supertraits.clone(); - - let (mut consts, mut methods, mut types) = (Vec::new(), Vec::new(), Vec::new()); - - i.items.iter().for_each(|item| match item { - TraitItem::Const(c) => consts.push(c.clone()), - TraitItem::Method(m) => methods.push(m.clone()), - TraitItem::Type(t) => types.push(t.clone()), - other => panic!("Found unexcepted trait item: `{:?}`", other), - }); - - TraitDefMetadata { - generics, - supertraits, - consts, - methods, - types, - } -} +use crate::diagnosis::DiagnosticGenerator; +use crate::rustdoc::types::{GenericBound, Generics, RustdocToCb}; +use rustdoc_types::Crate; #[derive(Clone, Debug, PartialEq)] pub(crate) struct TraitDefMetadata { - generics: Generics, - supertraits: Punctuated, - consts: Vec, - methods: Vec, - types: Vec, + pub is_auto: bool, + pub is_unsafe: bool, + pub generics: Generics, + pub bounds: Vec, } -impl From for ItemKind { - fn from(metadata: TraitDefMetadata) -> ItemKind { - ItemKind::TraitDef(metadata) - } -} +impl DiagnosticGenerator for TraitDefMetadata {} -impl DiagnosticGenerator for TraitDefMetadata { - fn modification_diagnosis( - &self, - other: &Self, - path: &ItemPath, - collector: &mut DiagnosisCollector, - ) { - if self.generics != other.generics || self.supertraits != other.supertraits { - collector.add(DiagnosisItem::modification(path.clone(), None)); +impl RustdocToCb for rustdoc_types::Trait { + fn to_cb(&self, data: &Crate) -> TraitDefMetadata { + TraitDefMetadata { + is_auto: self.is_auto, + is_unsafe: self.is_unsafe, + generics: self.generics.to_cb(data), + bounds: self.bounds.to_cb(data), } - - diagnosis_for_nameable( - self.consts.as_slice(), - other.consts.as_slice(), - path, - collector, - ); - - diagnosis_for_nameable( - self.methods.as_slice(), - other.methods.as_slice(), - path, - collector, - ); - - diagnosis_for_nameable( - self.types.as_slice(), - other.types.as_slice(), - path, - collector, - ); - } -} - -#[cfg(test)] -impl Parse for TraitDefMetadata { - fn parse(input: ParseStream) -> ParseResult { - input - .parse() - .map(|trait_def| extract_def_trait_metadata(&trait_def)) - } -} - -fn diagnosis_for_nameable( - left: &[Item], - right: &[Item], - path: &ItemPath, - collector: &mut DiagnosisCollector, -) where - Item: Nameable + PartialEq, -{ - for left_item in left { - let left_item_name = left_item.name(); - - match Item::find_named(right, left_item_name) { - Some(right_item) if left_item == right_item => {} - - altered => { - let path = ItemPath::extend(path.clone(), left_item_name.clone()); - let diagnostic_creator = if altered.is_some() { - DiagnosisItem::modification - } else { - DiagnosisItem::removal - }; - - let diagnosis = diagnostic_creator(path, None); - collector.add(diagnosis); - } - } - } - - for right_item in right { - let right_item_name = right_item.name(); - - if Item::find_named(left, right_item_name).is_none() { - let path = ItemPath::extend(path.clone(), right_item_name.clone()); - let diagnosis = DiagnosisItem::addition(path, None); - collector.add(diagnosis) - } - } -} - -trait Nameable: Sized { - fn name(&self) -> &Ident; - - fn find_named<'a>(items: &'a [Self], name: &Ident) -> Option<&'a Self> { - items.iter().find(|item| item.name() == name) - } -} - -impl Nameable for TraitItem { - fn name(&self) -> &Ident { - match self { - TraitItem::Const(c) => &c.ident, - TraitItem::Method(m) => &m.sig.ident, - TraitItem::Type(t) => &t.ident, - other => panic!("Found illegal trait item:\n{:#?}", other), - } - } -} - -impl Nameable for TraitItemConst { - fn name(&self) -> &Ident { - &self.ident - } -} - -impl Nameable for TraitItemMethod { - fn name(&self) -> &Ident { - &self.sig.ident - } -} - -impl Nameable for TraitItemType { - fn name(&self) -> &Ident { - &self.ident } } diff --git a/src/public_api/trait_impls.rs b/src/public_api/trait_impls.rs index 168af4a..6727fe8 100644 --- a/src/public_api/trait_impls.rs +++ b/src/public_api/trait_impls.rs @@ -1,192 +1,35 @@ -use std::collections::HashMap; +use crate::diagnosis::DiagnosticGenerator; +use crate::rustdoc::types::{Generics, RustdocToCb, Type}; +use derivative::Derivative; +use rustdoc_types::Crate; -use syn::{ - visit::{self, Visit}, - AngleBracketedGenericArguments, Generics, Ident, ImplItemConst, ImplItemType, ItemImpl, - ItemMod, -}; - -#[cfg(test)] -use syn::parse::{Parse, ParseStream, Result as ParseResult}; - -use crate::{ - diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}, - public_api::utils, -}; - -#[cfg(test)] -use crate::ast::CrateAst; - -use super::{imports::PathResolver, ItemKind, ItemPath}; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct TraitImplVisitor<'a> { - items: HashMap, - path: Vec, - resolver: &'a PathResolver, -} - -impl<'a> TraitImplVisitor<'a> { - pub(crate) fn new( - items: HashMap, - resolver: &'a PathResolver, - ) -> TraitImplVisitor<'a> { - let path = Vec::new(); - TraitImplVisitor { - items, - path, - resolver, - } - } - - pub(crate) fn items(self) -> HashMap { - self.items - } - - fn add_path_segment(&mut self, segment: Ident) { - self.path.push(segment); - } - - fn remove_path_segment(&mut self) { - self.path.pop().unwrap(); - } - - fn add_trait_impl(&mut self, type_path: &ItemPath, impl_: TraitImplMetadata) { - let type_ = self - .items - .get_mut(type_path) - .expect("Type not found") - .as_type_mut() - .expect("Can't impl a trait for a non-type item"); - - type_.add_trait_impl(impl_); - } -} - -impl<'a, 'ast> Visit<'ast> for TraitImplVisitor<'a> { - fn visit_item_mod(&mut self, mod_: &'ast ItemMod) { - self.add_path_segment(mod_.ident.clone()); - visit::visit_item_mod(self, mod_); - self.remove_path_segment(); - } - - fn visit_item_impl(&mut self, impl_: &'ast ItemImpl) { - let (type_name, trait_impl_metadata) = - match extract_impl_trait_metadata(impl_, self.resolver, self.path.as_slice()) { - Some(value) => value, - None => return, - }; - - self.add_trait_impl( - &ItemPath::concat_both(self.path.clone(), type_name.to_owned()), - trait_impl_metadata, - ); - } -} - -fn extract_impl_trait_metadata<'a>( - impl_: &ItemImpl, - resolver: &'a PathResolver, - current_path: &[Ident], -) -> Option<(&'a [Ident], TraitImplMetadata)> { - let trait_path = match &impl_.trait_ { - Some((_, trait_path, _)) => trait_path, - None => return None, - }; - - let (trait_name, trait_generic_args) = - utils::extract_name_and_generic_args_from_path(trait_path)?; - - let trait_name = trait_name.clone(); - let trait_generic_args = trait_generic_args.cloned(); - - let (type_path, type_generic_args) = - utils::extract_name_and_generic_args(impl_.self_ty.as_ref())?; - - let resolved_path = resolver.resolve(current_path, type_path)?; - let type_generic_args = type_generic_args.cloned(); - - let mut consts = Vec::new(); - let mut types = Vec::new(); - - for item in &impl_.items { - match item { - syn::ImplItem::Const(c) => consts.push(c.clone()), - syn::ImplItem::Type(t) => types.push(t.clone()), - _ => {} - } - } - - let generic_parameters = impl_.generics.clone(); - - let trait_impl_metadata = TraitImplMetadata { - trait_name, - generic_parameters, - trait_generic_args, - type_generic_args, - consts, - types, - }; - - Some((resolved_path, trait_impl_metadata)) -} - -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Derivative)] +#[derivative(PartialEq)] pub(crate) struct TraitImplMetadata { - trait_name: Ident, - generic_parameters: Generics, - trait_generic_args: Option, - type_generic_args: Option, - - consts: Vec, - types: Vec, -} - -impl TraitImplMetadata { - pub(crate) fn trait_name(&self) -> &Ident { - &self.trait_name - } -} - -impl DiagnosticGenerator for TraitImplMetadata { - fn removal_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { - collector.add(DiagnosisItem::removal( - path.clone(), - Some(self.trait_name.clone()), - )); - } - - fn modification_diagnosis( - &self, - _other: &Self, - path: &ItemPath, - collector: &mut DiagnosisCollector, - ) { - collector.add(DiagnosisItem::modification( - path.clone(), - Some(self.trait_name.clone()), - )); - } - - fn addition_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { - collector.add(DiagnosisItem::addition( - path.clone(), - Some(self.trait_name.clone()), - )); - } -} - -#[cfg(test)] -impl Parse for TraitImplMetadata { - fn parse(input: ParseStream) -> ParseResult { - let impl_ = input.fork().parse::()?; - let ast = input.parse::()?; - - let resolver = PathResolver::new(&ast); - - match extract_impl_trait_metadata(&impl_, &resolver, &[]) { - Some((_, metadata)) => Ok(metadata), - None => Err(input.error("Failed to parse trait implementation metadata")), + pub is_unsafe: bool, + pub generics: Generics, + #[derivative(PartialEq(compare_with = "crate::comparator::cmp_vec_unordered"))] + pub provided_trait_methods: Vec, + pub trait_: Option, + pub for_: Type, + pub negative: bool, + pub synthetic: bool, + pub blanket_impl: Option, +} + +impl DiagnosticGenerator for TraitImplMetadata {} + +impl RustdocToCb for rustdoc_types::Impl { + fn to_cb(&self, data: &Crate) -> TraitImplMetadata { + TraitImplMetadata { + is_unsafe: self.is_unsafe, + generics: self.generics.to_cb(data), + provided_trait_methods: self.provided_trait_methods.clone(), + trait_: self.trait_.as_ref().map(|t| t.to_cb(data)), + for_: self.for_.to_cb(data), + negative: self.negative, + synthetic: self.synthetic, + blanket_impl: self.blanket_impl.as_ref().map(|t| t.to_cb(data)), } } } diff --git a/src/public_api/types.rs b/src/public_api/types.rs index 53b9b20..a994f24 100644 --- a/src/public_api/types.rs +++ b/src/public_api/types.rs @@ -1,328 +1,149 @@ -use std::collections::HashMap; +use rustdoc_types::{Crate, Enum, Struct, StructType, Typedef, Union}; -use syn::{ - punctuated::Punctuated, - token::Comma, - visit::{self, Visit}, - Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, ItemEnum, ItemMod, ItemStruct, - Variant, Visibility, -}; +use crate::diagnosis::DiagnosticGenerator; -use tap::Conv; - -#[cfg(test)] -use syn::parse::{Parse, ParseStream, Result as ParseResult}; - -use crate::diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}; - -use super::{trait_impls::TraitImplMetadata, ItemKind, ItemPath}; - -#[derive(Clone, Debug, Default, PartialEq)] -pub(crate) struct TypeVisitor { - types: HashMap, - path: Vec, -} - -impl TypeVisitor { - pub(crate) fn new() -> TypeVisitor { - TypeVisitor::default() - } - - pub(crate) fn types(self) -> HashMap { - self.types - } - - fn add_path_segment(&mut self, segment: Ident) { - self.path.push(segment); - } - - fn remove_path_segment(&mut self) { - self.path.pop().unwrap(); - } - - fn add_type(&mut self, path: ItemPath, kind: ItemKind) { - let tmp = self.types.insert(path, kind); - assert!(tmp.is_none(), "Duplicate item definition"); - } -} - -impl<'ast> Visit<'ast> for TypeVisitor { - fn visit_item_mod(&mut self, mod_: &'ast ItemMod) { - if matches!(mod_.vis, Visibility::Public(_)) { - self.add_path_segment(mod_.ident.clone()); - visit::visit_item_mod(self, mod_); - self.remove_path_segment(); - } - } - - fn visit_item_struct(&mut self, i: &'ast ItemStruct) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let k = ItemPath::new(self.path.clone(), i.ident.clone()); - let v = StructMetadata::new(i.generics.clone(), i.fields.clone()) - .conv::() - .into(); - - self.add_type(k, v); - } - - fn visit_item_enum(&mut self, i: &'ast ItemEnum) { - if !matches!(i.vis, Visibility::Public(_)) { - return; - } - - let k = ItemPath::new(self.path.clone(), i.ident.clone()); - let v = EnumMetadata::new(i.generics.clone(), i.variants.clone()) - .conv::() - .into(); - - self.add_type(k, v); - } -} +use crate::rustdoc::types::{Generics, RustdocToCb, Type}; #[derive(Clone, Debug, PartialEq)] pub(crate) struct TypeMetadata { - inner: InnerTypeMetadata, - traits: Vec, -} - -#[cfg(test)] -impl TypeMetadata { - pub(crate) fn traits(&self) -> &[TraitImplMetadata] { - &self.traits - } + generics: Generics, + data: InnerTypeMetadata, } impl TypeMetadata { - fn new(inner: InnerTypeMetadata) -> TypeMetadata { - TypeMetadata { - inner, - traits: Vec::new(), - } - } - - pub(crate) fn add_trait_impl(&mut self, impl_: TraitImplMetadata) { - self.traits.push(impl_); - } - - fn find_trait(&self, name: &Ident) -> Option<&TraitImplMetadata> { - self.traits - .iter() - .find(|trait_| trait_.trait_name() == name) + fn new(generics: Generics, data: InnerTypeMetadata) -> TypeMetadata { + TypeMetadata { generics, data } } } -impl DiagnosticGenerator for TypeMetadata { - fn modification_diagnosis( - &self, - other: &Self, - path: &ItemPath, - collector: &mut DiagnosisCollector, - ) { - if self.inner != other.inner { - collector.add(DiagnosisItem::modification(path.clone(), None)); - } - - // TODO: replace these O(n²) zone with a faster implentation, perhaps by - // using an ordered list or a HashMap. - - for trait_1 in self.traits.iter() { - match other.find_trait(trait_1.trait_name()) { - Some(trait_2) if trait_1 == trait_2 => {} - - Some(_) => collector.add(DiagnosisItem::modification( - path.clone(), - Some(trait_1.trait_name().clone()), - )), - - None => collector.add(DiagnosisItem::removal( - path.clone(), - Some(trait_1.trait_name().clone()), - )), - } +#[derive(Clone, Debug)] +pub(crate) enum InnerTypeMetadata { + Struct { + struct_type: StructType, + fields_stripped: bool, + }, + Union { + fields_stripped: bool, + }, + Enum { + variants_stripped: bool, + }, + Typedef { + type_: Box, + }, +} + +impl PartialEq for InnerTypeMetadata { + fn eq(&self, other: &Self) -> bool { + // special case: when a type containing only public items/fields gets added a private field + // for structs and unions this breaks literals + // the change is not breaking, since it only *allows* new usages + if !self.is_stripped() && other.is_stripped() { + return false; } - for trait_2 in other.traits.iter() { - if self.find_trait(trait_2.trait_name()).is_none() { - collector.add(DiagnosisItem::addition( - path.clone(), - Some(trait_2.trait_name().clone()), - )); - } + use InnerTypeMetadata::*; + match (self, other) { + ( + Struct { struct_type, .. }, + Struct { + struct_type: struct_type_other, + .. + }, + ) => struct_type == struct_type_other, + (Typedef { type_ }, Typedef { type_: type_other }) => type_ == type_other, + _ => true, } } } -impl From for TypeMetadata { - fn from(s: StructMetadata) -> TypeMetadata { - TypeMetadata::new(s.into()) - } -} - -impl From for TypeMetadata { - fn from(e: EnumMetadata) -> Self { - TypeMetadata::new(e.into()) - } -} - -#[cfg(test)] -impl Parse for TypeMetadata { - fn parse(input: ParseStream) -> ParseResult { - Ok(TypeMetadata::new(input.parse()?)) - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum InnerTypeMetadata { - Struct(StructMetadata), - Enum(EnumMetadata), -} - -impl From for InnerTypeMetadata { - fn from(v: StructMetadata) -> InnerTypeMetadata { - InnerTypeMetadata::Struct(v) - } -} - -impl From for InnerTypeMetadata { - fn from(v: EnumMetadata) -> InnerTypeMetadata { - InnerTypeMetadata::Enum(v) - } -} - -#[cfg(test)] -impl Parse for InnerTypeMetadata { - fn parse(input: ParseStream) -> ParseResult { - input - .parse::() - .map(Into::into) - .or_else(|mut e| { - input.parse::().map(Into::into).map_err(|e_| { - e.combine(e_); - e - }) - }) +impl InnerTypeMetadata { + fn is_stripped(&self) -> bool { + match self { + InnerTypeMetadata::Struct { + fields_stripped, .. + } => *fields_stripped, + InnerTypeMetadata::Union { + fields_stripped, .. + } => *fields_stripped, + InnerTypeMetadata::Enum { + variants_stripped, .. + } => *variants_stripped, + InnerTypeMetadata::Typedef { .. } => false, + } } } #[derive(Clone, Debug, PartialEq)] -pub(crate) struct StructMetadata { - generics: Generics, - fields: Fields, +pub(crate) struct TypeFieldMetadata { + name: String, + ty: Type, } -impl StructMetadata { - fn new(generics: Generics, fields: Fields) -> StructMetadata { - let fields = fields.remove_private_fields(); - StructMetadata { generics, fields } +impl TypeFieldMetadata { + pub fn new(name: String, ty: Type) -> TypeFieldMetadata { + TypeFieldMetadata { name, ty } } } -#[cfg(test)] -impl Parse for StructMetadata { - fn parse(input: ParseStream) -> ParseResult { - let ItemStruct { - generics, fields, .. - } = input.parse()?; - - Ok(StructMetadata::new(generics, fields)) - } -} +impl DiagnosticGenerator for TypeFieldMetadata {} #[derive(Clone, Debug, PartialEq)] -pub(crate) struct EnumMetadata { - generics: Generics, - variants: Vec, +pub(crate) struct EnumVariantMetadata { + name: String, } -impl EnumMetadata { - fn new(generics: Generics, variants: Punctuated) -> EnumMetadata { - let variants = variants - .into_iter() - .map(Variant::remove_private_fields) - .collect(); - - EnumMetadata { generics, variants } +impl EnumVariantMetadata { + pub fn new(name: String) -> EnumVariantMetadata { + EnumVariantMetadata { name } } } -#[cfg(test)] -impl Parse for EnumMetadata { - fn parse(input: ParseStream) -> ParseResult { - let ItemEnum { - generics, variants, .. - } = input.parse()?; - let variants = variants.into_iter().collect(); - Ok(EnumMetadata { generics, variants }) - } -} +impl DiagnosticGenerator for EnumVariantMetadata {} -trait ContainsPrivateFields { - fn remove_private_fields(self) -> Self; -} - -impl ContainsPrivateFields for Variant { - fn remove_private_fields(self) -> Self { - let Variant { - attrs, - ident, - mut fields, - discriminant, - } = self; - fields = fields.remove_private_fields(); - - Variant { - attrs, - ident, - fields, - discriminant, - } +impl RustdocToCb for Struct { + fn to_cb(&self, data: &Crate) -> TypeMetadata { + TypeMetadata::new( + self.generics.to_cb(data), + InnerTypeMetadata::Struct { + struct_type: self.struct_type.clone(), + fields_stripped: self.fields_stripped, + }, + ) } } -impl ContainsPrivateFields for Fields { - fn remove_private_fields(self) -> Self { - match self { - Fields::Named(named) => Fields::Named(named.remove_private_fields()), - Fields::Unnamed(unnamed) => Fields::Unnamed(unnamed.remove_private_fields()), - Fields::Unit => Fields::Unit, - } +impl RustdocToCb for Union { + fn to_cb(&self, data: &Crate) -> TypeMetadata { + TypeMetadata::new( + self.generics.to_cb(data), + InnerTypeMetadata::Union { + fields_stripped: self.fields_stripped, + }, + ) } } -impl ContainsPrivateFields for FieldsNamed { - fn remove_private_fields(self) -> Self { - let FieldsNamed { - brace_token, - mut named, - } = self; - named = named.remove_private_fields(); - - FieldsNamed { brace_token, named } +impl RustdocToCb for Enum { + fn to_cb(&self, data: &Crate) -> TypeMetadata { + TypeMetadata::new( + self.generics.to_cb(data), + InnerTypeMetadata::Enum { + variants_stripped: self.variants_stripped, + }, + ) } } -impl ContainsPrivateFields for FieldsUnnamed { - fn remove_private_fields(self) -> Self { - let FieldsUnnamed { - paren_token, - mut unnamed, - } = self; - unnamed = unnamed.remove_private_fields(); - - FieldsUnnamed { - paren_token, - unnamed, - } +impl RustdocToCb for Typedef { + fn to_cb(&self, data: &Crate) -> TypeMetadata { + TypeMetadata::new( + self.generics.to_cb(data), + InnerTypeMetadata::Typedef { + type_: Box::new(self.type_.to_cb(data)), + }, + ) } } -impl ContainsPrivateFields for Punctuated { - fn remove_private_fields(self) -> Self { - self.into_iter() - .filter(|field| matches!(field.vis, Visibility::Public(_))) - .collect() - } -} +impl DiagnosticGenerator for TypeMetadata {} diff --git a/src/public_api/utils.rs b/src/public_api/utils.rs deleted file mode 100644 index de53ae1..0000000 --- a/src/public_api/utils.rs +++ /dev/null @@ -1,42 +0,0 @@ -use syn::{AngleBracketedGenericArguments, Ident, Path, PathArguments, Type, TypePath}; - -pub(crate) fn extract_name_and_generic_args( - ty: &Type, -) -> Option<(&Path, Option<&AngleBracketedGenericArguments>)> { - let path = match ty { - Type::Path(TypePath { path, .. }) => path, - // TODO: handle non-path types - _ => return None, - }; - - Some((path, extract_ending_generics(path))) -} - -fn extract_ending_generics(path: &Path) -> Option<&AngleBracketedGenericArguments> { - let last_argument = path.segments.last().map(|segment| &segment.arguments)?; - match last_argument { - PathArguments::AngleBracketed(args) => Some(args), - _ => None, - } -} - -pub(crate) fn extract_name_and_generic_args_from_path( - p: &Path, -) -> Option<(&Ident, Option<&AngleBracketedGenericArguments>)> { - let unique_segment = match p.segments.len() { - 1 => p.segments.first().unwrap(), - // TODO: handle paths with more than one segment in them - _ => return None, - }; - - let name = &unique_segment.ident; - - let generics = match &unique_segment.arguments { - syn::PathArguments::None => None, - syn::PathArguments::AngleBracketed(args) => Some(args), - // TODO: handle paths with parenthesis (for instance Fn(T) -> U). - syn::PathArguments::Parenthesized(_) => return None, - }; - - Some((name, generics)) -} diff --git a/src/rustdoc.rs b/src/rustdoc.rs new file mode 100644 index 0000000..2e5e1c8 --- /dev/null +++ b/src/rustdoc.rs @@ -0,0 +1,69 @@ +use std::io::Write; +use std::path::Path; +use std::{fs, io}; + +use crate::cli::config; +use anyhow::{bail, Context}; +use anyhow::{Error as AnyError, Result as AnyResult}; +use log::info; +use rustdoc_types::Crate; + +pub(crate) mod types; + +pub(crate) fn run(root: &Path, package_name: &str) -> AnyResult { + info!("Running rustdoc"); + + let mut cmd = std::process::Command::new("cargo"); + cmd.arg("+nightly").arg("rustdoc").arg("--lib"); + if config::get().all_features { + cmd.arg("--all-features"); + } + if config::get().no_default_features { + cmd.arg("--no-default-features"); + } + for feature in &config::get().features { + cmd.arg("--features").arg(feature); + } + if let Some(package) = &config::get().package { + cmd.arg("--package").arg(package); + } + cmd.arg("--") + .arg("-Zunstable-options") + .arg("-wjson") + .current_dir(root); + + let stat = if config::get().display_build_output { + cmd.status().context("Failed to run rustdoc")? + } else { + // capture output + let out = cmd + .output() + .map_err(AnyError::new) + .context("Failed to run rustdoc")?; + + // display it only if rustdoc failed + if !out.status.success() { + io::stdout().write_all(&out.stdout).unwrap(); + io::stderr().write_all(&out.stderr).unwrap(); + } + + out.status + }; + + if !stat.success() { + bail!("Rustdoc exited with non-zero status code"); + } + + let json_path = root + .join("target") + .join("doc") + .join(format!("{}.json", package_name)); + + let json = fs::read_to_string(json_path) + .map_err(AnyError::new) + .context("Failed to read rustdoc output")?; + + serde_json::from_str::(&json) + .map_err(AnyError::new) + .context("Failed to deserialize rustdoc output") +} diff --git a/src/rustdoc/types.rs b/src/rustdoc/types.rs new file mode 100644 index 0000000..b5648bb --- /dev/null +++ b/src/rustdoc/types.rs @@ -0,0 +1,760 @@ +use crate::public_api::{ItemPath, ItemSummaryKind}; +use derivative::Derivative; +use itertools::*; +use rustdoc_types::{Crate, Item}; +use std::fmt; +use std::fmt::Display; + +// Type definitions fetched from rustdoc_types +// Originally fetched from https://github.com/rust-lang/rust/blob/master/src/rustdoc-json-types/lib.rs +// Format version 15 + +#[derive(Clone, Debug, PartialEq)] +pub struct Id(ItemPath); + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Header { + pub const_: bool, + pub unsafe_: bool, + pub async_: bool, + pub abi: Abi, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Abi { + // We only have a concrete listing here for stable ABI's because their are so many + // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + System { unwind: bool }, + Other(String), +} + +#[derive(Clone, Debug, Derivative)] +#[derivative(PartialEq)] +pub enum Type { + /// Structs, enums, and traits + ResolvedPath { + name: String, + #[derivative(PartialEq = "ignore")] + id: Id, + #[derivative(PartialEq = "ignore")] + target: Option>, + args: Option>, + param_names: Vec, + }, + /// Parameterized types + Generic(String), + /// Fixed-size numeric types (plus int/usize/float), char, arrays, slices, and tuples + Primitive(String), + /// `extern "ABI" fn` + FunctionPointer(Box), + /// `(String, u32, Box)` + Tuple(Vec), + /// `[u32]` + Slice(Box), + /// [u32; 15] + Array { type_: Box, len: String }, + /// `impl TraitA + TraitB + ...` + ImplTrait(Vec), + /// `_` + Infer, + /// `*mut u32`, `*u8`, etc. + RawPointer { mutable: bool, type_: Box }, + /// `&'a mut String`, `&str`, etc. + BorrowedRef { + lifetime: Option, + mutable: bool, + type_: Box, + }, + /// `::Name` or associated types like `T::Item` where `T: Iterator` + QualifiedPath { + name: String, + args: Box, + self_type: Box, + trait_: Box, + }, +} + +impl RustdocToCb for rustdoc_types::Id { + fn to_cb(&self, data: &Crate) -> Id { + Id(self.to_cb(data)) + } +} + +impl RustdocToCb for rustdoc_types::Id { + fn to_cb(&self, data: &Crate) -> ItemPath { + match data.paths.get(self) { + Some(summary) => ItemPath::new(summary.path[1..].to_vec(), summary.kind.to_cb(data)), + None => ItemPath::new(vec![self.0.to_string()], ItemSummaryKind::Unknown), + } + } +} + +impl RustdocToCb for rustdoc_types::Type { + fn to_cb(&self, data: &Crate) -> Type { + match self { + rustdoc_types::Type::ResolvedPath { + name, + id, + args, + param_names, + } => Type::ResolvedPath { + name: name.clone(), + id: id.to_cb(data), + target: data.index.get(id).map(Clone::clone).map(Box::new), + args: args.as_ref().map(|a| a.to_cb(data)), + param_names: param_names.to_cb(data), + }, + rustdoc_types::Type::Generic(s) => Type::Generic(s.clone()), + rustdoc_types::Type::Primitive(name) => Type::Primitive(name.clone()), + rustdoc_types::Type::FunctionPointer(fp) => Type::FunctionPointer(fp.to_cb(data)), + rustdoc_types::Type::Tuple(t) => Type::Tuple(t.to_cb(data)), + rustdoc_types::Type::Slice(s) => Type::Slice(s.to_cb(data)), + rustdoc_types::Type::Array { type_, len } => Type::Array { + type_: type_.to_cb(data), + len: len.clone(), + }, + rustdoc_types::Type::ImplTrait(it) => Type::ImplTrait(it.to_cb(data)), + rustdoc_types::Type::Infer => Type::Infer, + rustdoc_types::Type::RawPointer { mutable, type_ } => Type::RawPointer { + mutable: *mutable, + type_: type_.to_cb(data), + }, + rustdoc_types::Type::BorrowedRef { + lifetime, + mutable, + type_, + } => Type::BorrowedRef { + lifetime: (*lifetime).clone(), + mutable: *mutable, + type_: type_.to_cb(data), + }, + rustdoc_types::Type::QualifiedPath { + name, + args, + self_type, + trait_, + } => Type::QualifiedPath { + name: name.clone(), + args: args.to_cb(data), + self_type: self_type.to_cb(data), + trait_: trait_.to_cb(data), + }, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionPointer { + pub decl: FnDecl, + pub generic_params: Vec, + pub header: Header, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericArgs { + /// <'a, 32, B: Copy, C = u32> + AngleBracketed { + args: Vec, + bindings: Vec, + }, + /// Fn(A, B) -> C + Parenthesized { + inputs: Vec, + output: Option, + }, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TypeBinding { + pub name: String, + pub args: GenericArgs, + pub binding: TypeBindingKind, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TypeBindingKind { + Equality(Term), + Constraint(Vec), +} + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericArg { + Lifetime(String), + Type(Type), + Const(Constant), + Infer, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct FnDecl { + pub inputs: Vec<(String, Type)>, + pub output: Option, + pub c_variadic: bool, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TraitBoundModifier { + None, + Maybe, + MaybeConst, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericBound { + TraitBound { + trait_: Type, + generic_params: Vec, + modifier: TraitBoundModifier, + }, + Outlives(String), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct GenericParamDef { + pub name: String, + pub kind: GenericParamDefKind, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericParamDefKind { + Lifetime { + outlives: Vec, + }, + Type { + bounds: Vec, + default: Option, + synthetic: bool, + }, + Const { + type_: Type, + default: Option, + }, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Generics { + pub params: Vec, + pub where_predicates: Vec, +} + +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Debug, PartialEq)] +pub enum WherePredicate { + BoundPredicate { + type_: Type, + bounds: Vec, + /// Used for Higher-Rank Trait Bounds (HRTBs) + /// ```plain + /// where for<'a> &'a T: Iterator," + /// ^^^^^^^ + /// | + /// this part + /// ``` + generic_params: Vec, + }, + RegionPredicate { + lifetime: String, + bounds: Vec, + }, + EqPredicate { + lhs: Type, + rhs: Term, + }, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Term { + Type(Box), + Constant(Constant), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Constant { + pub type_: Type, + pub expr: String, + pub value: Option, + pub is_literal: bool, +} + +pub(crate) trait RustdocToCb { + fn to_cb(&self, data: &Crate) -> T; +} + +impl RustdocToCb for rustdoc_types::Generics { + fn to_cb(&self, data: &Crate) -> Generics { + Generics { + params: self.params.to_cb(data), + where_predicates: self.where_predicates.to_cb(data), + } + } +} + +impl RustdocToCb for rustdoc_types::WherePredicate { + fn to_cb(&self, data: &Crate) -> WherePredicate { + match self { + rustdoc_types::WherePredicate::BoundPredicate { + type_, + bounds, + generic_params, + } => WherePredicate::BoundPredicate { + type_: type_.to_cb(data), + bounds: bounds.to_cb(data), + generic_params: generic_params.to_cb(data), + }, + rustdoc_types::WherePredicate::RegionPredicate { lifetime, bounds } => { + WherePredicate::RegionPredicate { + lifetime: lifetime.clone(), + bounds: bounds.to_cb(data), + } + } + rustdoc_types::WherePredicate::EqPredicate { lhs, rhs } => { + WherePredicate::EqPredicate { + lhs: lhs.to_cb(data), + rhs: rhs.to_cb(data), + } + } + } + } +} + +impl RustdocToCb for rustdoc_types::FnDecl { + fn to_cb(&self, data: &Crate) -> FnDecl { + FnDecl { + inputs: self + .inputs + .iter() + .map(|i| (i.0.clone(), i.1.to_cb(data))) + .collect(), + output: self.output.as_ref().map(|o| o.to_cb(data)), + c_variadic: self.c_variadic, + } + } +} + +impl RustdocToCb
for rustdoc_types::Header { + fn to_cb(&self, data: &Crate) -> Header { + Header { + const_: self.const_, + unsafe_: self.unsafe_, + async_: self.async_, + abi: self.abi.to_cb(data), + } + } +} + +impl RustdocToCb for rustdoc_types::Abi { + fn to_cb(&self, _data: &Crate) -> Abi { + match self { + rustdoc_types::Abi::Rust => Abi::Rust, + rustdoc_types::Abi::C { unwind } => Abi::C { unwind: *unwind }, + rustdoc_types::Abi::Cdecl { unwind } => Abi::Cdecl { unwind: *unwind }, + rustdoc_types::Abi::Stdcall { unwind } => Abi::Stdcall { unwind: *unwind }, + rustdoc_types::Abi::Fastcall { unwind } => Abi::Fastcall { unwind: *unwind }, + rustdoc_types::Abi::Aapcs { unwind } => Abi::Aapcs { unwind: *unwind }, + rustdoc_types::Abi::Win64 { unwind } => Abi::Win64 { unwind: *unwind }, + rustdoc_types::Abi::SysV64 { unwind } => Abi::SysV64 { unwind: *unwind }, + rustdoc_types::Abi::System { unwind } => Abi::System { unwind: *unwind }, + rustdoc_types::Abi::Other(name) => Abi::Other(name.to_string()), + } + } +} + +impl RustdocToCb for rustdoc_types::FunctionPointer { + fn to_cb(&self, data: &Crate) -> FunctionPointer { + FunctionPointer { + decl: self.decl.to_cb(data), + generic_params: self.generic_params.to_cb(data), + header: self.header.to_cb(data), + } + } +} + +impl RustdocToCb for rustdoc_types::GenericArgs { + fn to_cb(&self, data: &Crate) -> GenericArgs { + match self { + rustdoc_types::GenericArgs::AngleBracketed { args, bindings } => { + GenericArgs::AngleBracketed { + args: args.to_cb(data), + bindings: bindings.to_cb(data), + } + } + rustdoc_types::GenericArgs::Parenthesized { inputs, output } => { + GenericArgs::Parenthesized { + inputs: inputs.to_cb(data), + output: output.as_ref().map(|o| o.to_cb(data)), + } + } + } + } +} + +impl RustdocToCb for rustdoc_types::TypeBinding { + fn to_cb(&self, data: &Crate) -> TypeBinding { + TypeBinding { + name: self.name.clone(), + args: self.args.to_cb(data), + binding: self.binding.to_cb(data), + } + } +} + +impl RustdocToCb for rustdoc_types::TypeBindingKind { + fn to_cb(&self, data: &Crate) -> TypeBindingKind { + match self { + rustdoc_types::TypeBindingKind::Equality(t) => TypeBindingKind::Equality(t.to_cb(data)), + rustdoc_types::TypeBindingKind::Constraint(t) => { + TypeBindingKind::Constraint(t.to_cb(data)) + } + } + } +} + +impl RustdocToCb for rustdoc_types::TraitBoundModifier { + fn to_cb(&self, _data: &Crate) -> TraitBoundModifier { + match self { + rustdoc_types::TraitBoundModifier::None => TraitBoundModifier::None, + rustdoc_types::TraitBoundModifier::Maybe => TraitBoundModifier::Maybe, + rustdoc_types::TraitBoundModifier::MaybeConst => TraitBoundModifier::MaybeConst, + } + } +} + +impl RustdocToCb for rustdoc_types::GenericBound { + fn to_cb(&self, data: &Crate) -> GenericBound { + match self { + rustdoc_types::GenericBound::TraitBound { + trait_, + generic_params, + modifier, + } => GenericBound::TraitBound { + trait_: trait_.to_cb(data), + generic_params: generic_params.to_cb(data), + modifier: modifier.to_cb(data), + }, + rustdoc_types::GenericBound::Outlives(t) => GenericBound::Outlives(t.clone()), + } + } +} + +impl RustdocToCb for rustdoc_types::GenericParamDef { + fn to_cb(&self, data: &Crate) -> GenericParamDef { + GenericParamDef { + name: self.name.clone(), + kind: self.kind.to_cb(data), + } + } +} + +impl RustdocToCb for rustdoc_types::GenericParamDefKind { + fn to_cb(&self, data: &Crate) -> GenericParamDefKind { + match self { + rustdoc_types::GenericParamDefKind::Lifetime { outlives } => { + GenericParamDefKind::Lifetime { + outlives: outlives.clone(), + } + } + rustdoc_types::GenericParamDefKind::Type { + bounds, + default, + synthetic, + } => GenericParamDefKind::Type { + bounds: bounds.to_cb(data), + default: default.as_ref().map(|d| d.to_cb(data)), + synthetic: *synthetic, + }, + rustdoc_types::GenericParamDefKind::Const { type_, default } => { + GenericParamDefKind::Const { + type_: type_.to_cb(data), + default: (*default).clone(), + } + } + } + } +} + +impl RustdocToCb for rustdoc_types::Term { + fn to_cb(&self, data: &Crate) -> Term { + match self { + rustdoc_types::Term::Type(t) => Term::Type(Box::new(t.to_cb(data))), + rustdoc_types::Term::Constant(c) => Term::Constant(c.to_cb(data)), + } + } +} + +impl RustdocToCb for rustdoc_types::GenericArg { + fn to_cb(&self, data: &Crate) -> GenericArg { + match self { + rustdoc_types::GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt.clone()), + rustdoc_types::GenericArg::Type(t) => GenericArg::Type(t.to_cb(data)), + rustdoc_types::GenericArg::Const(c) => GenericArg::Const(c.to_cb(data)), + rustdoc_types::GenericArg::Infer => GenericArg::Infer, + } + } +} + +impl RustdocToCb for rustdoc_types::Constant { + fn to_cb(&self, data: &Crate) -> Constant { + Constant { + type_: self.type_.to_cb(data), + expr: self.expr.clone(), + value: self.value.clone(), + is_literal: self.is_literal, + } + } +} + +impl RustdocToCb> for Vec +where + T: RustdocToCb, +{ + fn to_cb(&self, data: &Crate) -> Vec { + self.iter().map(|i| i.to_cb(data)).collect() + } +} + +impl RustdocToCb> for Box +where + T: RustdocToCb, +{ + fn to_cb(&self, data: &Crate) -> Box { + let inner: &T = self; + Box::new(inner.to_cb(data)) + } +} + +impl Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Type::ResolvedPath { name, args, .. } => { + write!(f, "{}", name)?; + if let Some(args) = args { + write!(f, "{}", args)?; + } + Ok(()) + } + Type::Generic(s) => write!(f, "{}", s), + Type::Primitive(p) => write!(f, "{}", p), + Type::FunctionPointer(p) => write!(f, "{}", p), + Type::Tuple(types) => { + write!(f, "({})", types.iter().join(", ")) + } + Type::Slice(t) => write!(f, "[{}]", t), + Type::Array { type_, len } => write!(f, "[{}; {}]", type_, len), + Type::ImplTrait(traits) => { + write!(f, "impl {}", traits.iter().join(" + ")) + } + Type::Infer => write!(f, "_"), + Type::RawPointer { mutable, type_ } => { + write!(f, "*{}{}", if *mutable { "mut " } else { "" }, type_) + } + Type::BorrowedRef { + lifetime, + mutable, + type_, + } => { + let lifetime = match lifetime { + Some(l) => format!("{} ", l), + None => "".to_owned(), + }; + write!( + f, + "&{}{}{}", + lifetime, + if *mutable { "mut " } else { "" }, + type_ + ) + } + Type::QualifiedPath { .. } => { + todo!() + } + } + } +} + +impl Display for Header { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut qualifiers = Vec::new(); + if self.const_ { + qualifiers.push("const"); + } + if self.async_ { + qualifiers.push("async"); + } + if self.unsafe_ { + qualifiers.push("unsafe"); + } + write!(f, "{}{}", qualifiers.join(" "), self.abi) + } +} + +impl Display for Abi { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = match self { + Abi::Rust => return Ok(()), + Abi::C { .. } => "C", + Abi::Cdecl { .. } => "cdecl", + Abi::Stdcall { .. } => "stdcall", + Abi::Fastcall { .. } => "fastcall", + Abi::Aapcs { .. } => "aapcs", + Abi::Win64 { .. } => "win64", + Abi::SysV64 { .. } => "sysv64", + Abi::System { .. } => "system", + Abi::Other(s) => s, + }; + write!(f, " extern \"{}\"", name) + } +} + +impl Display for FunctionPointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if !self.generic_params.is_empty() { + write!(f, "for<{}> ", self.generic_params.iter().join(", "))?; + } + write!( + f, + "{} fn({})", + self.header, + self.decl + .inputs + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty)) + .join(", ") + )?; + if let Some(output) = &self.decl.output { + write!(f, " -> {}", output)?; + } + Ok(()) + } +} + +impl Display for GenericArgs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GenericArgs::AngleBracketed { args, bindings } => { + if args.is_empty() && bindings.is_empty() { + return Ok(()); + } + write!(f, "<{}", args.iter().join(", "))?; + if !args.is_empty() && !bindings.is_empty() { + write!(f, ", ")?; + } + write!(f, "{}>", bindings.iter().join(", ")) + } + GenericArgs::Parenthesized { inputs, output } => { + write!(f, "Fn({})", inputs.iter().join(", "))?; + if let Some(output) = output { + write!(f, " -> ")?; + write!(f, "{}", output)?; + } + Ok(()) + } + } + } +} + +impl Display for TypeBinding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{}", self.name, self.args)?; + match &self.binding { + TypeBindingKind::Equality(ty) => write!(f, " = {}", ty), + TypeBindingKind::Constraint(bounds) => { + write!(f, ": {}", bounds.iter().join(" + ")) + } + } + } +} + +impl Display for Term { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Term::Type(ty) => write!(f, "{}", ty), + Term::Constant(c) => write!(f, "{}", c), + } + } +} + +impl Display for GenericArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GenericArg::Lifetime(lt) => write!(f, "{}", lt), + GenericArg::Type(t) => write!(f, "{}", t), + GenericArg::Const(c) => write!(f, "{}", c), + GenericArg::Infer => write!(f, "_"), + } + } +} + +impl Display for Constant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.expr) + } +} + +impl Display for GenericBound { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GenericBound::TraitBound { + trait_, + generic_params, + modifier, + } => { + write!(f, "{}{}", modifier, trait_)?; + if !generic_params.is_empty() { + write!(f, "<{}>", generic_params.iter().join(", "))?; + } + Ok(()) + } + GenericBound::Outlives(t) => write!(f, "{}", t), + } + } +} + +impl Display for TraitBoundModifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TraitBoundModifier::None => Ok(()), + TraitBoundModifier::Maybe => write!(f, "?"), + TraitBoundModifier::MaybeConst => write!(f, "?const "), + } + } +} + +impl Display for GenericParamDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name)?; + match &self.kind { + GenericParamDefKind::Lifetime { outlives } => { + if !outlives.is_empty() { + write!(f, ": {}", outlives.join(", "))?; + } + Ok(()) + } + GenericParamDefKind::Type { + bounds, default, .. + } => { + if !bounds.is_empty() { + write!(f, ": {}", bounds.iter().join(" + "))?; + } + if let Some(default) = default { + write!(f, " = {}", default)?; + } + Ok(()) + } + GenericParamDefKind::Const { default, .. } => { + // TODO: what to do of type? + if let Some(default) = default { + write!(f, " = {}", default)?; + } + Ok(()) + } + } + } +} diff --git a/tests/enum.rs b/tests/enum.rs index 36d7342..418a693 100644 --- a/tests/enum.rs +++ b/tests/enum.rs @@ -1,9 +1,8 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; +use cargo_breaking::tests::get_diff; #[test] fn not_reported_when_private() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { enum A {} @@ -15,19 +14,19 @@ fn not_reported_when_private() { #[test] fn new_enum() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { pub enum A {} }, }; - assert_eq!(diff.to_string(), "+ A\n"); + assert_eq!(diff.to_string(), "+ A (enum)\n"); } #[test] fn new_named_variant_field_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub enum A { B {} @@ -36,68 +35,47 @@ fn new_named_variant_field_is_modification() { { pub enum A { B { - pub c: u8, + c: u8, } } }, }; - assert_eq!(diff.to_string(), "≠ A\n"); + assert_eq!(diff.to_string(), "+ A::B::c (struct field)\n"); } #[test] fn new_unnamed_variant_field_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub enum A { - B() } + B() + } }, { pub enum A { - B(pub u8) + B(u8) } }, }; - assert_eq!(diff.to_string(), "≠ A\n"); + assert_eq!(diff.to_string(), "+ A::B::0 (struct field)\n"); } #[test] fn named_field_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub enum A { - B(pub u8), + B(u8), } }, { pub enum A { - B(pub u16), + B(u16), } } }; - assert_eq!(diff.to_string(), "≠ A\n"); -} - -#[test] -fn empty_variant_kind_change_is_modification() { - let files = [ - "pub enum A { B }", - "pub enum A { B() }", - "pub enum A { B {} }", - ]; - - for (id_a, file_a) in files.iter().enumerate() { - for (id_b, file_b) in files.iter().enumerate() { - let comparator = cargo_breaking::compare(file_a, file_b).unwrap(); - let diff = comparator.run(); - - if id_a != id_b { - assert_eq!(diff.to_string(), "≠ A\n"); - } else { - assert!(diff.is_empty()); - } - } - } + assert_eq!(diff.to_string(), "≠ A::B::0 (struct field)\n"); } diff --git a/tests/function.rs b/tests/function.rs index aabb168..c66f1e8 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -1,12 +1,11 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; +use cargo_breaking::tests::get_diff; #[test] fn private_is_not_reported() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { - fn fact(n: u32) -> u32 {} + fn fact(n: u32) -> u32 { todo!() } }, }; @@ -15,19 +14,19 @@ fn private_is_not_reported() { #[test] fn addition() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { - pub fn fact(n: u32) -> u32 {} + pub fn fact(n: u32) -> u32 { todo!() } }, }; - assert_eq!(diff.to_string(), "+ fact\n"); + assert_eq!(diff.to_string(), "+ fact (function)\n"); } #[test] fn new_arg() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub fn fact() {} }, @@ -36,12 +35,12 @@ fn new_arg() { } }; - assert_eq!(diff.to_string(), "≠ fact\n"); + assert_eq!(diff.to_string(), "≠ fact (function)\n"); } #[test] fn generic_order() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub fn f() {} }, @@ -50,12 +49,12 @@ fn generic_order() { }, }; - assert_eq!(diff.to_string(), "≠ f\n"); + assert_eq!(diff.to_string(), "≠ f (function)\n"); } #[test] fn body_change_not_detected() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub fn fact() {} }, @@ -67,69 +66,55 @@ fn body_change_not_detected() { assert!(diff.is_empty()); } -#[test] -fn fn_arg_comma_is_removed() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub fn a(a: t, b: t, c: t,) {} - }, - { - pub fn a(a: t, b: t, c: t) {} - }, - }; - - assert!(diff.is_empty()); -} - #[test] fn fn_arg_last_character_not_removed() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { - pub fn a(a: t, b: t, c: t) {} + pub fn a(a: u8, b: u8, c: u8) {} }, { - pub fn a(a: t, b: t, c: u) {} + pub fn a(a: u8, b: u8, c: u16) {} }, }; - assert_eq!(diff.to_string(), "≠ a\n"); -} - -#[test] -fn empty_struct_kind_change_is_modification() { - let files = ["pub struct A;", "pub struct A();", "pub struct A {}"]; - - for (id_a, file_a) in files.iter().enumerate() { - for (id_b, file_b) in files.iter().enumerate() { - let comparator = cargo_breaking::compare(file_a, file_b).unwrap(); - let diff = comparator.run(); - - if id_a != id_b { - assert_eq!(diff.to_string(), "≠ A\n"); - } else { - assert!(diff.is_empty()); - } - } - } + assert_eq!(diff.to_string(), "≠ a (function)\n"); } #[test] fn is_reported_lexicographically() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { pub fn a() {} pub fn z() {} } }; - assert_eq!(diff.to_string(), "+ a\n+ z\n"); + assert_eq!(diff.to_string(), "+ a (function)\n+ z (function)\n"); - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { pub fn z() {} pub fn a() {} } }; - assert_eq!(diff.to_string(), "+ a\n+ z\n"); + assert_eq!(diff.to_string(), "+ a (function)\n+ z (function)\n"); +} + +#[test] +fn custom_type_equality() { + let diff = get_diff! { + { + pub struct A { pub a: u8 } + + pub fn test(a: A) {} + }, + { + pub struct A { pub a: u16 } + + pub fn test(a: A) {} + }, + }; + + assert_eq!(diff.to_string(), "≠ A::a (struct field)\n"); } diff --git a/tests/method.rs b/tests/method.rs index b9a601a..b555a83 100644 --- a/tests/method.rs +++ b/tests/method.rs @@ -1,9 +1,8 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; +use cargo_breaking::tests::get_diff; #[test] fn new_public_method_is_addition() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub struct A; }, @@ -16,12 +15,12 @@ fn new_public_method_is_addition() { }, }; - assert_eq!(diff.to_string(), "+ A::a\n"); + assert_eq!(diff.to_string(), "+ A::a (method)\n"); } #[test] fn new_private_method_is_not_reported() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub struct A; }, @@ -39,7 +38,7 @@ fn new_private_method_is_not_reported() { #[test] fn method_removal_is_removal() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub struct A; @@ -54,12 +53,12 @@ fn method_removal_is_removal() { } }; - assert_eq!(diff.to_string(), "- A::a\n"); + assert_eq!(diff.to_string(), "- A::a (method)\n"); } #[test] fn signature_change_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub struct A; @@ -76,12 +75,12 @@ fn signature_change_is_modification() { }, }; - assert_eq!(diff.to_string(), "≠ A::f\n"); + assert_eq!(diff.to_string(), "≠ A::f (method)\n"); } #[test] -fn generic_param_change_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { +fn generic_param_change_is_not_modification() { + let diff = get_diff! { { pub struct A; impl A { @@ -96,34 +95,41 @@ fn generic_param_change_is_modification() { } }; - assert_eq!(diff.to_string(), "≠ A::f\n"); + assert!(diff.is_empty()); } #[test] fn generic_arg_change_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { - pub struct A; + pub struct A { + t: T, + } - impl A { + impl A { pub fn f() {} } }, { - pub struct A; + pub struct A { + t: T, + } - impl A { + impl A { pub fn f() {} } }, }; - assert_eq!(diff.to_string(), "≠ A::f\n"); + assert_eq!( + diff.to_string(), + "- A::::f (method)\n+ A::::f (method)\n" + ); } #[test] fn not_reported_when_type_is_not_public() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { struct A; @@ -143,7 +149,7 @@ fn not_reported_when_type_is_not_public() { #[test] fn is_reported_in_type_definition_path_1() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub mod foo { pub struct Bar; @@ -160,12 +166,12 @@ fn is_reported_in_type_definition_path_1() { }, }; - assert_eq!(diff.to_string(), "- foo::Bar::f\n"); + assert_eq!(diff.to_string(), "- foo::Bar::f (method)\n"); } #[test] fn is_reported_in_type_definition_path_2() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub mod foo { pub struct Bar; @@ -184,5 +190,5 @@ fn is_reported_in_type_definition_path_2() { } }; - assert_eq!(diff.to_string(), "- foo::Bar::f\n"); + assert_eq!(diff.to_string(), "- baz (module)\n- foo::Bar::f (method)\n"); } diff --git a/tests/struct.rs b/tests/struct.rs new file mode 100644 index 0000000..61f2664 --- /dev/null +++ b/tests/struct.rs @@ -0,0 +1,244 @@ +use cargo_breaking::tests::get_diff; + +#[test] +fn private_is_not_reported() { + let diff = get_diff! { + {}, + { + struct A; + }, + }; + + assert!(diff.is_empty()); +} + +#[test] +fn addition() { + let diff = get_diff! { + {}, + { + pub struct A; + } + }; + + assert_eq!(diff.to_string(), "+ A (struct)\n"); +} + +#[test] +fn removal() { + let diff = get_diff! { + { + pub struct B; + }, + {} + }; + + assert_eq!(diff.to_string(), "- B (struct)\n"); +} + +#[test] +fn new_public_field_tupled_is_modification() { + let diff = get_diff! { + { + pub struct C; + }, + { + pub struct C(pub u8); + }, + }; + + assert_eq!(diff.to_string(), "≠ C (struct)\n+ C::0 (struct field)\n"); +} + +#[test] +fn new_private_field_tupled_is_modification() { + let diff = get_diff! { + { + pub struct C(); + }, + { + pub struct C(usize); + }, + }; + + assert_eq!(diff.to_string(), "≠ C (struct)\n"); +} + +#[test] +fn remove_private_field_tupled_is_not_modification() { + let diff = get_diff! { + { + pub struct C(usize); + }, + { + pub struct C(); + }, + }; + + assert!(diff.is_empty()); +} + +#[test] +fn new_public_field_named_is_modification() { + let diff = get_diff! { + { + pub struct D {} + }, + { + pub struct D { + pub a: u8, + } + }, + }; + + assert_eq!(diff.to_string(), "+ D::a (struct field)\n"); +} + +#[test] +fn new_private_field_named_is_not_reported() { + let diff = get_diff! { + { + pub struct D { + a: u8, + } + }, + { + pub struct D { + a: u8, + c: u8, + } + }, + }; + + assert!(diff.is_empty()); +} + +#[test] +fn new_private_field_named_with_existing_public_is_modification() { + let diff = get_diff! { + { + pub struct D { + pub a: u8, + } + }, + { + pub struct D { + pub a: u8, + c: u8, + } + }, + }; + + assert_eq!(diff.to_string(), "≠ D (struct)\n"); +} + +#[test] +fn remove_private_field_named_with_existing_public_is_modification() { + let diff = get_diff! { + { + pub struct D { + pub a: u8, + c: u8, + } + }, + { + pub struct D { + pub a: u8, + } + }, + }; + + assert!(diff.is_empty()); +} + +#[test] +fn public_named_field_modification() { + let diff = get_diff! { + { + pub struct A { + pub a: u8, + } + }, + { + pub struct A { + pub a: u16, + } + }, + }; + + assert_eq!(diff.to_string(), "≠ A::a (struct field)\n"); +} + +#[test] +fn public_unnamed_field_modification() { + let diff = get_diff! { + { + pub struct A(pub u8); + }, + { + pub struct A(pub u16); + }, + }; + + assert_eq!(diff.to_string(), "≠ A::0 (struct field)\n"); +} + +#[test] +fn public_named_field_removal_is_modification() { + let diff = get_diff! { + { + pub struct A { + pub a: u8, + } + }, + { + pub struct A {} + }, + }; + + assert_eq!(diff.to_string(), "- A::a (struct field)\n"); +} + +#[test] +fn public_unnamed_field_removal_is_modification() { + let diff = get_diff! { + { + pub struct A(pub u8); + }, + { + pub struct A(); + }, + }; + + assert_eq!(diff.to_string(), "- A::0 (struct field)\n"); +} + +#[test] +fn generic_change_is_modification() { + let diff = get_diff! { + { + pub struct E; + }, + { + pub struct E { f: T } + }, + }; + + assert_eq!(diff.to_string(), "≠ E (struct)\n"); +} + +#[test] +fn whole_struct_removal() { + let diff = get_diff! { + { + pub struct A { + pub a: u8, + } + }, + { + + }, + }; + + assert_eq!(diff.to_string(), "- A (struct)\n"); +} diff --git a/tests/structs.rs b/tests/structs.rs deleted file mode 100644 index 3600157..0000000 --- a/tests/structs.rs +++ /dev/null @@ -1,177 +0,0 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; - -#[test] -fn private_is_not_reported() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - {}, - { - struct A; - }, - }; - - assert!(diff.is_empty()); -} - -#[test] -fn addition() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - {}, - { - pub struct A; - } - }; - - assert_eq!(diff.to_string(), "+ A\n"); -} - -#[test] -fn removal() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct B; - }, - {} - }; - - assert_eq!(diff.to_string(), "- B\n"); -} - -#[test] -fn new_public_field_tupled_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct C; - }, - { - pub struct C(pub u8); - }, - }; - - assert_eq!(diff.to_string(), "≠ C\n"); -} - -#[test] -fn new_private_field_tupled_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct C(); - }, - { - pub struct C(usize); - }, - }; - - assert!(diff.is_empty()); -} - -#[test] -fn new_public_field_named_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct D {} - }, - { - pub struct D { - pub a: u8, - } - }, - }; - - assert_eq!(diff.to_string(), "≠ D\n"); -} - -#[test] -fn new_private_field_named_is_not_reported() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct D { - a: b, - } - }, - { - pub struct D { - a: b, - c: d, - } - }, - }; - - assert!(diff.is_empty()); -} - -#[test] -fn public_named_field_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct A { - pub a: u8, - } - }, - { - pub struct A { - pub a: u16, - } - }, - }; - - assert_eq!(diff.to_string(), "≠ A\n"); -} - -#[test] -fn public_unnamed_field_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct A(pub u8); - }, - { - pub struct A(pub u16); - }, - }; - - assert_eq!(diff.to_string(), "≠ A\n"); -} - -#[test] -fn public_named_field_removal_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct A { - pub a: u8, - } - }, - { - pub struct A {} - }, - }; - - assert_eq!(diff.to_string(), "≠ A\n"); -} - -#[test] -fn public_unnamed_field_removal_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct A(pub u8); - }, - { - pub struct A(); - }, - }; - - assert_eq!(diff.to_string(), "≠ A\n"); -} - -#[test] -fn generic_change_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { - { - pub struct E; - }, - { - pub struct E; - }, - }; - - assert_eq!(diff.to_string(), "≠ E\n"); -} diff --git a/tests/trait_defs.rs b/tests/trait_def.rs similarity index 58% rename from tests/trait_defs.rs rename to tests/trait_def.rs index ac6d986..6a9fe59 100644 --- a/tests/trait_defs.rs +++ b/tests/trait_def.rs @@ -1,21 +1,20 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; +use cargo_breaking::tests::get_diff; #[test] fn addition_simple() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { pub trait A {} } }; - assert_eq!(diff.to_string(), "+ A\n"); + assert_eq!(diff.to_string(), "+ A (trait)\n"); } #[test] fn trait_item_addition() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub trait A {} }, @@ -24,30 +23,30 @@ fn trait_item_addition() { }, }; - assert_eq!(diff.to_string(), "+ A::B\n"); + assert_eq!(diff.to_string(), "+ A::B (associated type)\n"); } #[test] fn trait_item_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub trait A { - type B = u8; + const B: u8 = 5; } }, { pub trait A { - type B = u16; + const B: u8 = 6; } }, }; - assert_eq!(diff.to_string(), "≠ A::B\n"); + assert_eq!(diff.to_string(), "≠ A::B (associated constant)\n"); } #[test] fn trait_item_removal() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub trait A { type B; @@ -58,12 +57,12 @@ fn trait_item_removal() { }, }; - assert_eq!(diff.to_string(), "- A::B\n"); + assert_eq!(diff.to_string(), "- A::B (associated type)\n"); } #[test] fn trait_item_kind_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub trait A { type B; @@ -76,12 +75,15 @@ fn trait_item_kind_modification() { }, }; - assert_eq!(diff.to_string(), "- A::B\n+ A::B\n"); + assert_eq!( + diff.to_string(), + "- A::B (associated type)\n+ A::B (associated constant)\n" + ); } #[test] fn in_private_module() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { {}, { mod a { @@ -95,7 +97,7 @@ fn in_private_module() { #[test] fn in_public_module() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { pub mod a {} }, @@ -106,5 +108,5 @@ fn in_public_module() { }, }; - assert_eq!(diff.to_string(), "+ a::A\n"); + assert_eq!(diff.to_string(), "+ a::A (trait)\n"); } diff --git a/tests/trait_impls.rs b/tests/trait_impl.rs similarity index 51% rename from tests/trait_impls.rs rename to tests/trait_impl.rs index 7301e81..9a88e08 100644 --- a/tests/trait_impls.rs +++ b/tests/trait_impl.rs @@ -1,49 +1,66 @@ -use cargo_breaking::ApiCompatibilityDiagnostics; -use syn::parse_quote; +use cargo_breaking::tests::get_diff; #[test] fn addition_simple() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait A {} + pub struct T; }, { + pub trait A {} + pub struct T; impl A for T {} }, }; - assert_eq!(diff.to_string(), "+ T: A\n"); + assert_eq!(diff.to_string(), "+ T::[impl A] (impl)\n"); } #[test] fn modification_simple() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { - pub struct T; + pub trait A {} - impl A for T {} + pub struct T { + u: U, + } + + impl A for T {} }, { - pub struct T; + pub trait A {} - impl A for T {} + pub struct T { + u: U, + } + + impl A for T {} } }; - assert_eq!(diff.to_string(), "≠ T: A\n"); + assert_eq!(diff.to_string(), "≠ T::[impl A] (impl)\n"); } #[test] fn provided_method_implementation_is_not_reported() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait T {} + pub struct S; impl T for S {} }, { + pub trait T { + fn f(); + } + pub struct S; impl T for S { @@ -52,13 +69,17 @@ fn provided_method_implementation_is_not_reported() { }, }; - assert!(diff.is_empty()); + assert_eq!(diff.to_string(), "+ T::f (method)\n"); } #[test] fn constant_modification_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait T { + const C: usize; + } + pub struct S; impl T for S { @@ -66,6 +87,10 @@ fn constant_modification_is_modification() { } }, { + pub trait T { + const C: usize; + } + pub struct S; impl T for S { @@ -74,41 +99,55 @@ fn constant_modification_is_modification() { }, }; - assert_eq!(diff.to_string(), "≠ S: T\n"); + assert_eq!(diff.to_string(), "≠ S::[impl T]::C (associated constant)\n"); } #[test] fn type_modification_is_modification() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait T { + type C; + } + pub struct S; impl T for S { - type T = u8; + type C = u8; } }, { + pub trait T { + type C; + } + pub struct S; impl T for S { - type T = u16; + type C = u16; } }, }; - assert_eq!(diff.to_string(), "≠ S: T\n"); + assert_eq!(diff.to_string(), "≠ S::[impl T]::C (associated type)\n"); } #[test] fn impl_trait_order_is_not_tracked() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait T1 {} + pub trait T2 {} + pub struct S; impl T1 for S {} impl T2 for S {} }, { + pub trait T1 {} + pub trait T2 {} + pub struct S; impl T2 for S {} @@ -121,13 +160,17 @@ fn impl_trait_order_is_not_tracked() { #[test] fn not_reported_when_type_is_not_public() { - let diff: ApiCompatibilityDiagnostics = parse_quote! { + let diff = get_diff! { { + pub trait T {} + struct S; impl T for S {} }, { + pub trait T {} + struct S; } }; From f2b9619c8780ffcc823e1ed62c1795bb39bed4a5 Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Fri, 8 Jul 2022 15:57:52 +0200 Subject: [PATCH 2/4] Remove irrelevant paragraph from the AST backend --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index 9a46d51..1b85f7f 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,8 @@
Logo is provided by Morgane Gaillard (
@Arlune) under the MPL license.
- - - `cargo-breaking` compares a crate's public API between two different branches, shows what changed, and suggests the next version according to [semver][semver]. @@ -73,20 +70,13 @@ It currently detects the following: - implementations, including methods and associated items, - trait definitions. -As we compare parts of the crate AST, it reports a lot of false positives: - -- renaming an argument is reported as a breaking change, -- renaming a generic type is reported as a breaking change, -- adding a generic type with a default value is a breaking change, -- depending on the situation, adding a trailing comma may be a breaking change. - [semver]: https://semver.org/ [add-field-pg]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=492a1727404d1f8d199962c639454f44 [ania]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_not_impl_any.html ## Contribution -A book is maintained to help understanding how the crate works, and what are its inner parts and their behaviour. +A book is maintained to help understand how the crate works, and what are its inner parts and their behaviour. It can be found here : [book](https://iomentum.github.io/cargo-breaking/) From 4543cdb91bdcdd91256c73729ce79f94e3abfd28 Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Fri, 8 Jul 2022 16:44:01 +0200 Subject: [PATCH 3/4] Update book --- book/src/1_foreword.md | 14 +- book/src/2_0_process.md | 2 +- book/src/2_1_overview.md | 19 +- book/src/2_2_config.md | 10 +- book/src/2_3_manifest.md | 5 + book/src/2_3_repository.md | 3 - book/src/2_4_crate.md | 8 + book/src/2_4_manifest.md | 3 - book/src/2_5_api_extraction.md | 3 - book/src/2_5_rustdoc.md | 22 +++ book/src/2_6_comparator.md | 3 - book/src/2_6_comparison.md | 40 ++++ book/src/2_7_diagnosis.md | 3 - book/src/2_7_next_version.md | 13 ++ book/src/2_8_next_version.md | 3 - book/src/SUMMARY.md | 11 +- docs/1_foreword.html | 37 ++-- docs/2_0_process.html | 26 ++- docs/2_1_overview.html | 51 ++--- docs/2_2_config.html | 38 +++- ...{2_3_repository.html => 2_3_manifest.html} | 35 +++- ...2_5_api_extraction.html => 2_4_crate.html} | 42 ++-- docs/{2_4_manifest.html => 2_5_rustdoc.html} | 53 +++-- docs/2_6_comparator.html | 181 ------------------ ...2_7_diagnosis.html => 2_6_comparison.html} | 57 ++++-- ...ext_version.html => 2_7_next_version.html} | 41 +++- docs/3_book.html | 28 ++- docs/404.html | 26 ++- docs/CNAME | 1 - docs/ayu-highlight.css | 1 - docs/book.js | 35 +++- docs/css/chrome.css | 49 ++++- docs/css/general.css | 18 +- docs/css/variables.css | 6 +- docs/highlight.css | 1 - docs/index.html | 37 ++-- docs/print.html | 144 +++++++++----- docs/searchindex.js | 2 +- docs/searchindex.json | 2 +- docs/tomorrow-night.css | 2 - 40 files changed, 651 insertions(+), 424 deletions(-) create mode 100644 book/src/2_3_manifest.md delete mode 100644 book/src/2_3_repository.md create mode 100644 book/src/2_4_crate.md delete mode 100644 book/src/2_4_manifest.md delete mode 100644 book/src/2_5_api_extraction.md create mode 100644 book/src/2_5_rustdoc.md delete mode 100644 book/src/2_6_comparator.md create mode 100644 book/src/2_6_comparison.md delete mode 100644 book/src/2_7_diagnosis.md create mode 100644 book/src/2_7_next_version.md delete mode 100644 book/src/2_8_next_version.md rename docs/{2_3_repository.html => 2_3_manifest.html} (85%) rename docs/{2_5_api_extraction.html => 2_4_crate.html} (81%) rename docs/{2_4_manifest.html => 2_5_rustdoc.html} (76%) delete mode 100644 docs/2_6_comparator.html rename docs/{2_7_diagnosis.html => 2_6_comparison.html} (74%) rename docs/{2_8_next_version.html => 2_7_next_version.html} (82%) delete mode 100644 docs/CNAME diff --git a/book/src/1_foreword.md b/book/src/1_foreword.md index 62a45c0..22e00c9 100644 --- a/book/src/1_foreword.md +++ b/book/src/1_foreword.md @@ -1,6 +1,6 @@ # Foreword -This book's goal is to hold and maintain informations on how the innards of cargo-breaking works together to compare two versions of a library and display the differences between both. +The goal of this book is to help you understand the inner-workings of cargo-breaking and how it compares two versions of a Rust project to display the differences between both. Example: @@ -9,7 +9,7 @@ $ cargo breaking - user::User::from_str ≠ user::User + user::User::from_path -+ user::User: Debug ++ user::User::[impl Debug] Next version is: 3.0.0 ``` @@ -31,10 +31,6 @@ upgrading from a previous version. ### Git workflow -Most work is commited in separate branch, before getting merged to `main` all -at once, once we're satisfied with the refactoring, fixes, and features added. -These branches are named `scrabsha/iter-dd-mm-yy`, representing the date at -which the iteration is started (for instance, `scrabsha/iter-19-06-21`). - -Installing `cargo-breaking` from the following branches give you the latest -changes. It may have instabilities, though. +Most work is done in separate branches, before getting merged to `main` all +at once, once the quality of the code is judged to be good enough. The branches +usually follow the naming convention `author/feature-name`. \ No newline at end of file diff --git a/book/src/2_0_process.md b/book/src/2_0_process.md index b17af25..c696e0b 100644 --- a/book/src/2_0_process.md +++ b/book/src/2_0_process.md @@ -1,3 +1,3 @@ # Process -This section describes the flow of the cargo-breaking application. +This section describes the execution flow of cargo-breaking. diff --git a/book/src/2_1_overview.md b/book/src/2_1_overview.md index 558a5c6..55059b5 100644 --- a/book/src/2_1_overview.md +++ b/book/src/2_1_overview.md @@ -2,16 +2,9 @@ The process used by cargo-breaking can be summarized like this: -- [2.2](./2_2_config.md): The configuration is parsed from the cli args - -- [2.3](./2_3_repository.md): The git repository informations are created from the env - -- [2.4](./2_4_manifest.md): The crate version is fetched from the manifest - -- [2.5](./2_5_api_extraction.md): The "current library" and the "target library to run against" are collected as AST with rustc - -- [2.6](./2_6_comparator.md): Both libraries are compared against each other to collect removals, additions and modifications - -- [2.7](./2_7_diagnosis.md): The results are gathered in a diagnosis structure - -- [2.8](./2_8_next_version.md): The "best" next version is suggested from the diagnosis +- [2.2](./2_2_config.md): The configuration is parsed from the CLI args and other configuration sources +- [2.3](./2_3_manifest.md): The crate metadata is read from the manifest file +- [2.4](./2_4_crate.md): The source code for the previous and the next version is fetched using Git in a temporary directory +- [2.5](./2_5_rustdoc.md): Both codebases are ran through rustdoc and the JSON output is then deserialized +- [2.6](./2_6_comparison.md): The comparison is performed and the output is collected as a list of differences +- [2.7](./2_7_next_version.md): The "best" next version is suggested from the diagnosis list diff --git a/book/src/2_2_config.md b/book/src/2_2_config.md index af3f097..9c7259e 100644 --- a/book/src/2_2_config.md +++ b/book/src/2_2_config.md @@ -1,3 +1,11 @@ # Configuration -WORK IN PROGRESS \ No newline at end of file +cargo-breaking supports loading configuration data from the following sources, in order of priority (highest to lowest): +- Command-line parameters, using [clap](https://github.com/clap-rs/clap) +- Environment variables, with the prefix `CARGO_BREAKING_` +- The `cargo-breaking.toml` file +- The `cargo-breaking.json` file + +Configuration files are searched for in the current working directory. + +The loaded configuration is exposed via `cli::config`'s `get() -> &'static ProgramConfig` function. \ No newline at end of file diff --git a/book/src/2_3_manifest.md b/book/src/2_3_manifest.md new file mode 100644 index 0000000..0e6bb13 --- /dev/null +++ b/book/src/2_3_manifest.md @@ -0,0 +1,5 @@ +# Manifest file + +cargo-breaking searches for the `Cargo.toml` file in the current working directory, or in a subdirectory if a package is specified via `-p` / `--package`. + +The file is then read and deserialized, and the crate's package name and version is fetched. \ No newline at end of file diff --git a/book/src/2_3_repository.md b/book/src/2_3_repository.md deleted file mode 100644 index bc5af3e..0000000 --- a/book/src/2_3_repository.md +++ /dev/null @@ -1,3 +0,0 @@ -# Repository - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/2_4_crate.md b/book/src/2_4_crate.md new file mode 100644 index 0000000..a7c8090 --- /dev/null +++ b/book/src/2_4_crate.md @@ -0,0 +1,8 @@ +# Code fetching + +Once the crate's metadata has been decoded, cargo-breaking fetches the code for the previous and the next version in a +temporary directory. This is done by copying the `.git` folder in two different directories, and then checking out +respectively the revspec specified via `-a` / `--against` and the `HEAD` revspec. + +cargo-breaking internally supports a second loading source, which is used for loading raw code files directly. This is +used by the test harness to compare two single-file libraries. \ No newline at end of file diff --git a/book/src/2_4_manifest.md b/book/src/2_4_manifest.md deleted file mode 100644 index 6270d91..0000000 --- a/book/src/2_4_manifest.md +++ /dev/null @@ -1,3 +0,0 @@ -# Manifest - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/2_5_api_extraction.md b/book/src/2_5_api_extraction.md deleted file mode 100644 index ddccfe7..0000000 --- a/book/src/2_5_api_extraction.md +++ /dev/null @@ -1,3 +0,0 @@ -# API Extraction - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/2_5_rustdoc.md b/book/src/2_5_rustdoc.md new file mode 100644 index 0000000..662fd43 --- /dev/null +++ b/book/src/2_5_rustdoc.md @@ -0,0 +1,22 @@ +# Rustdoc execution + +The following command is executed in each of the two codebases: + +```shell +cargo +nightly rustdoc --lib ... -- -Zunstable-options -wjson +``` +where `...` represent additional optional Cargo arguments depending on the settings: +- `--features` +- `--no-default-features` +- `--all-features` + +Rustdoc writes its output in the `target/doc/{package_name}.json` file, which is then read and deserialized into a +`Crate` object. + +## Types handling + +The `rustdoc_types` crate contain copies of the Rustdoc internal type hierarchy, directly extracted from the rustc +repository. This allows reading the type without having to depend on the whole rustc codebase. + +However, because of the processing we are doing on the Rustdoc output, we ourselves had to make copies of those types, +to make some changes in the way some things are stored, for example IDs. \ No newline at end of file diff --git a/book/src/2_6_comparator.md b/book/src/2_6_comparator.md deleted file mode 100644 index dbeb12b..0000000 --- a/book/src/2_6_comparator.md +++ /dev/null @@ -1,3 +0,0 @@ -# Comparator - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/2_6_comparison.md b/book/src/2_6_comparison.md new file mode 100644 index 0000000..9f70166 --- /dev/null +++ b/book/src/2_6_comparison.md @@ -0,0 +1,40 @@ +# Comparison + +The `Crate` object from Rustdoc is converted to a `PublicApi` object, which is a flat hashmap of all public items in +the library. + +## Translation + +The `Crate` object is traversed and all its items associated to the root crate (ID 0) are processed. + +### Functions + +Free functions and methods yield `Fn` and `Method` items, respectively. + +### Types + +Structures, unions and enums yield `Type` items, and their fields and impls are also traversed. + +#### Fields + +Fields yield `Field` items. + +#### Impls + +Impl items are processed and yield either `Method`, `AssocType` or `AssocConst` items. + +If the impl is for a trait, it yields a `TraitImpl` item. + +### Trait definitions + +Trait definitions yield `TraitDef` items, and their items are processed in the same way as for impl items. + +### Modules + +Modules yield `Module` items. + +Their items are not directly processed since they already appear in the `Crate` index. + +### Typedefs + +Typedefs yield `Type` items. \ No newline at end of file diff --git a/book/src/2_7_diagnosis.md b/book/src/2_7_diagnosis.md deleted file mode 100644 index 816c215..0000000 --- a/book/src/2_7_diagnosis.md +++ /dev/null @@ -1,3 +0,0 @@ -# Diagnosis - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/2_7_next_version.md b/book/src/2_7_next_version.md new file mode 100644 index 0000000..ebdbced --- /dev/null +++ b/book/src/2_7_next_version.md @@ -0,0 +1,13 @@ +# Next version + +If the diagnosis contains breaking changes, the major version is incremented. Otherwise, if it contains additions, +the minor version is incremented. Otherwise, the patch version is incremented. + +## Breaking changes + +Currently, only modifications are considered breaking changes. + +**Note:** additions of public fields to a structure also yield a modification of the structure itself. The additions +are not internally considered breaking, the structure's modification is. This is to avoid having to handle different +kinds of additions in the comparison phase; we can simply emit a modification if we detect that the addition is +breaking. \ No newline at end of file diff --git a/book/src/2_8_next_version.md b/book/src/2_8_next_version.md deleted file mode 100644 index 9ab56e4..0000000 --- a/book/src/2_8_next_version.md +++ /dev/null @@ -1,3 +0,0 @@ -# Next Version - -WORK IN PROGRESS \ No newline at end of file diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 844f489..022da96 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -4,10 +4,9 @@ - [Process](./2_0_process.md) - [Overview](./2_1_overview.md) - [Configuration](./2_2_config.md) - - [Repository](./2_3_repository.md) - - [Manifest](./2_4_manifest.md) - - [API Extraction](./2_5_api_extraction.md) - - [Comparator](./2_6_comparator.md) - - [Diagnosis](./2_7_diagnosis.md) - - [Next Version](./2_8_next_version.md) + - [Manifest file](./2_3_manifest.md) + - [Code fetching](./2_4_crate.md) + - [Rustdoc execution](./2_5_rustdoc.md) + - [Comparison](./2_6_comparison.md) + - [Next version](./2_7_next_version.md) - [Book](./3_book.md) diff --git a/docs/1_foreword.html b/docs/1_foreword.html index 520d084..b22d5d9 100644 --- a/docs/1_foreword.html +++ b/docs/1_foreword.html @@ -4,9 +4,10 @@ Foreword - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_0_process.html b/docs/2_0_process.html index 1ec22ac..09a8fc9 100644 --- a/docs/2_0_process.html +++ b/docs/2_0_process.html @@ -4,9 +4,10 @@ Process - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_1_overview.html b/docs/2_1_overview.html index 411365c..07aec5c 100644 --- a/docs/2_1_overview.html +++ b/docs/2_1_overview.html @@ -4,9 +4,10 @@ Overview - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_2_config.html b/docs/2_2_config.html index db66b91..55e0690 100644 --- a/docs/2_2_config.html +++ b/docs/2_2_config.html @@ -4,9 +4,10 @@ Configuration - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_3_repository.html b/docs/2_3_manifest.html similarity index 85% rename from docs/2_3_repository.html rename to docs/2_3_manifest.html index c5dc2ac..78d85fb 100644 --- a/docs/2_3_repository.html +++ b/docs/2_3_manifest.html @@ -3,10 +3,11 @@ - Repository - Guide to cargo-breaking Development + Manifest file - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_5_api_extraction.html b/docs/2_4_crate.html similarity index 81% rename from docs/2_5_api_extraction.html rename to docs/2_4_crate.html index 03649cb..dd9e147 100644 --- a/docs/2_5_api_extraction.html +++ b/docs/2_4_crate.html @@ -3,10 +3,11 @@ - API Extraction - Guide to cargo-breaking Development + Code fetching - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_4_manifest.html b/docs/2_5_rustdoc.html similarity index 76% rename from docs/2_4_manifest.html rename to docs/2_5_rustdoc.html index a95527a..10f2f36 100644 --- a/docs/2_4_manifest.html +++ b/docs/2_5_rustdoc.html @@ -3,10 +3,11 @@ - Manifest - Guide to cargo-breaking Development + Rustdoc execution - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_6_comparator.html b/docs/2_6_comparator.html deleted file mode 100644 index 2d17523..0000000 --- a/docs/2_6_comparator.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - Comparator - Guide to cargo-breaking Development - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - -
-
-

Comparator

-

WORK IN PROGRESS

- -
- - -
-
- - - -
- - - - - - - - - - - - diff --git a/docs/2_7_diagnosis.html b/docs/2_6_comparison.html similarity index 74% rename from docs/2_7_diagnosis.html rename to docs/2_6_comparison.html index 0605f15..94b6e3a 100644 --- a/docs/2_7_diagnosis.html +++ b/docs/2_6_comparison.html @@ -3,10 +3,11 @@ - Diagnosis - Guide to cargo-breaking Development + Comparison - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/2_8_next_version.html b/docs/2_7_next_version.html similarity index 82% rename from docs/2_8_next_version.html rename to docs/2_7_next_version.html index 6caa90c..4113810 100644 --- a/docs/2_8_next_version.html +++ b/docs/2_7_next_version.html @@ -3,10 +3,11 @@ - Next Version - Guide to cargo-breaking Development + Next version - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/3_book.html b/docs/3_book.html index ef99b64..b30a008 100644 --- a/docs/3_book.html +++ b/docs/3_book.html @@ -4,9 +4,10 @@ Book - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/404.html b/docs/404.html index b2775e8..6d3e8fe 100644 --- a/docs/404.html +++ b/docs/404.html @@ -3,11 +3,12 @@ - + Page not found - Guide to cargo-breaking Development + + - @@ -19,15 +20,18 @@ + + + @@ -78,7 +82,7 @@ @@ -86,8 +90,7 @@
- - + @@ -127,6 +131,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 9b801ed..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -book.cargo-breaking.com \ No newline at end of file diff --git a/docs/ayu-highlight.css b/docs/ayu-highlight.css index 0c45c6f..32c9432 100644 --- a/docs/ayu-highlight.css +++ b/docs/ayu-highlight.css @@ -8,7 +8,6 @@ Original by Dempfi (https://github.com/dempfi/ayu) overflow-x: auto; background: #191f26; color: #e6e1cf; - padding: 0.5em; } .hljs-comment, diff --git a/docs/book.js b/docs/book.js index 5e38636..d40440c 100644 --- a/docs/book.js +++ b/docs/book.js @@ -108,9 +108,12 @@ function playground_text(playground) { let text = playground_text(code_block); let classes = code_block.querySelector('code').classList; - let has_2018 = classes.contains("edition2018"); - let edition = has_2018 ? "2018" : "2015"; - + let edition = "2015"; + if(classes.contains("edition2018")) { + edition = "2018"; + } else if(classes.contains("edition2021")) { + edition = "2021"; + } var params = { version: "stable", optimize: "0", @@ -133,7 +136,15 @@ function playground_text(playground) { body: JSON.stringify(params) }) .then(response => response.json()) - .then(response => result_block.innerText = response.result) + .then(response => { + if (response.result.trim() === '') { + result_block.innerText = "No output"; + result_block.classList.add("result-no-output"); + } else { + result_block.innerText = response.result; + result_block.classList.remove("result-no-output"); + } + }) .catch(error => result_block.innerText = "Playground Communication: " + error.message); } @@ -151,12 +162,13 @@ function playground_text(playground) { if (window.ace) { // language-rust class needs to be removed for editable // blocks or highlightjs will capture events - Array - .from(document.querySelectorAll('code.editable')) + code_nodes + .filter(function (node) {return node.classList.contains("editable"); }) .forEach(function (block) { block.classList.remove('language-rust'); }); Array - .from(document.querySelectorAll('code:not(.editable)')) + code_nodes + .filter(function (node) {return !node.classList.contains("editable"); }) .forEach(function (block) { hljs.highlightBlock(block); }); } else { code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); @@ -359,7 +371,14 @@ function playground_text(playground) { }); themePopup.addEventListener('click', function (e) { - var theme = e.target.id || e.target.parentElement.id; + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else if (e.target.parentElement.className === "theme") { + theme = e.target.parentElement.id; + } else { + return; + } set_theme(theme); }); diff --git a/docs/css/chrome.css b/docs/css/chrome.css index 21c08b9..53a54c8 100644 --- a/docs/css/chrome.css +++ b/docs/css/chrome.css @@ -208,24 +208,63 @@ pre { pre > .buttons { position: absolute; z-index: 100; - right: 5px; - top: 5px; + right: 0px; + top: 2px; + margin: 0px; + padding: 2px 0px; color: var(--sidebar-fg); cursor: pointer; + visibility: hidden; + opacity: 0; + transition: visibility 0.1s linear, opacity 0.1s linear; +} +pre:hover > .buttons { + visibility: visible; + opacity: 1 } pre > .buttons :hover { color: var(--sidebar-active); + border-color: var(--icons-hover); + background-color: var(--theme-hover); } pre > .buttons i { margin-left: 8px; } pre > .buttons button { - color: inherit; - background: transparent; - border: none; cursor: inherit; + margin: 0px 5px; + padding: 3px 5px; + font-size: 14px; + + border-style: solid; + border-width: 1px; + border-radius: 4px; + border-color: var(--icons); + background-color: var(--theme-popup-bg); + transition: 100ms; + transition-property: color,border-color,background-color; + color: var(--icons); } +@media (pointer: coarse) { + pre > .buttons button { + /* On mobile, make it easier to tap buttons. */ + padding: 0.3rem 1rem; + } +} +code { + padding: 1rem; +} + +/* FIXME: ACE editors overlap their buttons because ACE does absolute + positioning within the code block which breaks padding. The only solution I + can think of is to move the padding to the outer pre tag (or insert a div + wrapper), but that would require fixing a whole bunch of CSS rules. +*/ +.hljs.ace_editor { + padding: 0rem 0rem; +} + pre > .result { margin-top: 10px; } diff --git a/docs/css/general.css b/docs/css/general.css index 2cf347f..0e4f07a 100644 --- a/docs/css/general.css +++ b/docs/css/general.css @@ -12,6 +12,7 @@ html { color: var(--fg); background-color: var(--bg); text-size-adjust: none; + -webkit-text-size-adjust: none; } body { @@ -25,6 +26,16 @@ code { font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ } +/* make long words/inline code not x overflow */ +main { + overflow-wrap: break-word; +} + +/* make wide tables scroll if they overflow */ +.table-wrapper { + overflow-x: auto; +} + /* Don't change font size in headers. */ h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { font-size: unset; @@ -79,8 +90,7 @@ h6:target::before { .content { overflow-y: auto; - padding: 0 15px; - padding-bottom: 50px; + padding: 0 5px 50px 5px; } .content main { margin-left: auto; @@ -175,3 +185,7 @@ blockquote { margin: 5px 0px; font-weight: bold; } + +.result-no-output { + font-style: italic; +} diff --git a/docs/css/variables.css b/docs/css/variables.css index 9ff64d6..56b634b 100644 --- a/docs/css/variables.css +++ b/docs/css/variables.css @@ -67,7 +67,7 @@ --links: #2b79a2; - --inline-code-color: #c5c8c6;; + --inline-code-color: #c5c8c6; --theme-popup-bg: #141617; --theme-popup-border: #43484d; @@ -147,7 +147,7 @@ --links: #2b79a2; - --inline-code-color: #c5c8c6;; + --inline-code-color: #c5c8c6; --theme-popup-bg: #161923; --theme-popup-border: #737480; @@ -228,7 +228,7 @@ --links: #2b79a2; - --inline-code-color: #c5c8c6;; + --inline-code-color: #c5c8c6; --theme-popup-bg: #141617; --theme-popup-border: #43484d; diff --git a/docs/highlight.css b/docs/highlight.css index c234322..ba57b82 100644 --- a/docs/highlight.css +++ b/docs/highlight.css @@ -61,7 +61,6 @@ overflow-x: auto; background: #f6f7f6; color: #000; - padding: 0.5em; } .hljs-emphasis { diff --git a/docs/index.html b/docs/index.html index 8d5c992..b22d5d9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,9 +4,10 @@ Foreword - Guide to cargo-breaking Development + + - @@ -18,15 +19,18 @@ + + + @@ -77,7 +81,7 @@ @@ -85,8 +89,7 @@
- - + @@ -126,6 +130,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/print.html b/docs/print.html index 411afc3..09d16a1 100644 --- a/docs/print.html +++ b/docs/print.html @@ -5,9 +5,10 @@ Guide to cargo-breaking Development + + - @@ -19,15 +20,18 @@ + + + @@ -78,7 +82,7 @@ @@ -86,8 +90,7 @@
- - + @@ -127,6 +131,7 @@

Guide to cargo-breaking Development

+ + + + + + diff --git a/docs/searchindex.js b/docs/searchindex.js index e2869dd..5ed7296 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Object.assign(window.search, {"doc_urls":["1_foreword.html#foreword","1_foreword.html#installation","1_foreword.html#git-workflow","2_0_process.html#process","2_1_overview.html#overview","2_2_config.html#configuration","2_3_repository.html#repository","2_4_manifest.html#manifest","2_5_api_extraction.html#api-extraction","2_6_comparator.html#comparator","2_7_diagnosis.html#diagnosis","2_8_next_version.html#next-version","3_book.html#book","3_book.html#building-the-book"],"index":{"documentStore":{"docInfo":{"0":{"body":29,"breadcrumbs":2,"title":1},"1":{"body":34,"breadcrumbs":2,"title":1},"10":{"body":2,"breadcrumbs":3,"title":1},"11":{"body":2,"breadcrumbs":5,"title":2},"12":{"body":14,"breadcrumbs":2,"title":1},"13":{"body":16,"breadcrumbs":3,"title":2},"2":{"body":41,"breadcrumbs":3,"title":2},"3":{"body":6,"breadcrumbs":2,"title":1},"4":{"body":52,"breadcrumbs":3,"title":1},"5":{"body":2,"breadcrumbs":3,"title":1},"6":{"body":2,"breadcrumbs":3,"title":1},"7":{"body":2,"breadcrumbs":3,"title":1},"8":{"body":2,"breadcrumbs":5,"title":2},"9":{"body":2,"breadcrumbs":3,"title":1}},"docs":{"0":{"body":"This book's goal is to hold and maintain informations on how the innards of cargo-breaking works together to compare two versions of a library and display the differences between both. Example: $ cargo breaking\n- user::User::from_str\n≠ user::User\n+ user::User::from_path\n+ user::User: Debug Next version is: 3.0.0","breadcrumbs":"Foreword » Foreword","id":"0","title":"Foreword"},"1":{"body":"cargo-breaking needs the nightly toolchain to be installed to work correctly, but can be compiled with any toolchain. It can be compiled from sources with the following commands: $ git clone https://github.com/iomentum/cargo-breaking\n$ cd cargo-breaking\n$ cargo install --path ./ You may need to add the --force argument to the last command if you're upgrading from a previous version.","breadcrumbs":"Foreword » Installation","id":"1","title":"Installation"},"10":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Diagnosis » Diagnosis","id":"10","title":"Diagnosis"},"11":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Next Version » Next Version","id":"11","title":"Next Version"},"12":{"body":"Mdbook is needed to get running this book: $ cargo install mdbook\n$ cd book\n$ mdbook serve --dest-dir ../docs","breadcrumbs":"Book » Book","id":"12","title":"Book"},"13":{"body":"This updates the book so it is updated on push. // TODO! add this as a pre-commit hook $ cd book\n$ mdbook build --dest-dir ../docs","breadcrumbs":"Book » Building the book","id":"13","title":"Building the book"},"2":{"body":"Most work is commited in separate branch, before getting merged to main all at once, once we're satisfied with the refactoring, fixes, and features added. These branches are named scrabsha/iter-dd-mm-yy, representing the date at which the iteration is started (for instance, scrabsha/iter-19-06-21). Installing cargo-breaking from the following branches give you the latest changes. It may have instabilities, though.","breadcrumbs":"Foreword » Git workflow","id":"2","title":"Git workflow"},"3":{"body":"This section describes the flow of the cargo-breaking application.","breadcrumbs":"Process » Process","id":"3","title":"Process"},"4":{"body":"The process used by cargo-breaking can be summarized like this: 2.2 : The configuration is parsed from the cli args 2.3 : The git repository informations are created from the env 2.4 : The crate version is fetched from the manifest 2.5 : The \"current library\" and the \"target library to run against\" are collected as AST with rustc 2.6 : Both libraries are compared against each other to collect removals, additions and modifications 2.7 : The results are gathered in a diagnosis structure 2.8 : The \"best\" next version is suggested from the diagnosis","breadcrumbs":"Process » Overview » Overview","id":"4","title":"Overview"},"5":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Configuration » Configuration","id":"5","title":"Configuration"},"6":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Repository » Repository","id":"6","title":"Repository"},"7":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Manifest » Manifest","id":"7","title":"Manifest"},"8":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » API Extraction » API Extraction","id":"8","title":"API Extraction"},"9":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Comparator » Comparator","id":"9","title":"Comparator"}},"length":14,"save":true},"fields":["title","body","breadcrumbs"],"index":{"body":{"root":{"0":{"6":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"1":{"9":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"8":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"1":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"13":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"0":{"tf":1.0}}},"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.7320508075688772}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"2":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":3,"docs":{"0":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.0},"5":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"4":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"2":{"tf":1.0}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"1":{"tf":1.0},"2":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"13":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}}},"n":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0}}},"n":{"c":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"m":{"df":1,"docs":{"2":{"tf":1.0}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"12":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"11":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"o":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"13":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":2,"docs":{"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.0},"4":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}}}}},"s":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}},"c":{"df":0,"docs":{},"r":{"a":{"b":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"a":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"12":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"13":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"o":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":1,"docs":{"4":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"11":{"tf":1.0},"4":{"tf":1.4142135623730951}}}}}}}}},"w":{"df":0,"docs":{},"e":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":10,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"10":{"tf":1.0},"11":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"breadcrumbs":{"root":{"0":{"6":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"1":{"9":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"8":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"1":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"13":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"0":{"tf":1.0}}},"df":2,"docs":{"12":{"tf":2.23606797749979},"13":{"tf":2.23606797749979}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"2":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":3,"docs":{"0":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772}}}}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"2":{"tf":1.0}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"1":{"tf":1.0},"2":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":3,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0},"2":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"1":{"tf":1.0},"2":{"tf":1.4142135623730951},"4":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"13":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}}},"n":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"1":{"tf":2.0},"12":{"tf":1.0},"2":{"tf":1.0}}},"n":{"c":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.7320508075688772}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"m":{"df":1,"docs":{"2":{"tf":1.0}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"12":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"11":{"tf":1.7320508075688772},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"o":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.7320508075688772}}}}}}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"13":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"3":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.7320508075688772}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.0},"4":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}}}}},"s":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}},"c":{"df":0,"docs":{},"r":{"a":{"b":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"a":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"12":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"13":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"o":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":1,"docs":{"4":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"11":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951}}}}}}}}},"w":{"df":0,"docs":{},"e":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":10,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"10":{"tf":1.0},"11":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}}}}}}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"title":{"root":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}}},"m":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"11":{"tf":1.0}}}}}}}}},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}}); \ No newline at end of file +Object.assign(window.search, {"doc_urls":["1_foreword.html#foreword","1_foreword.html#installation","1_foreword.html#git-workflow","2_0_process.html#process","2_1_overview.html#overview","2_2_config.html#configuration","2_3_manifest.html#manifest-file","2_4_crate.html#code-fetching","2_5_rustdoc.html#rustdoc-execution","2_5_rustdoc.html#types-handling","2_6_comparison.html#comparison","2_6_comparison.html#translation","2_6_comparison.html#functions","2_6_comparison.html#types","2_6_comparison.html#trait-definitions","2_6_comparison.html#modules","2_6_comparison.html#typedefs","2_7_next_version.html#next-version","2_7_next_version.html#breaking-changes","3_book.html#book","3_book.html#building-the-book"],"index":{"documentStore":{"docInfo":{"0":{"body":28,"breadcrumbs":2,"title":1},"1":{"body":34,"breadcrumbs":2,"title":1},"10":{"body":11,"breadcrumbs":3,"title":1},"11":{"body":10,"breadcrumbs":3,"title":1},"12":{"body":8,"breadcrumbs":3,"title":1},"13":{"body":28,"breadcrumbs":3,"title":1},"14":{"body":11,"breadcrumbs":4,"title":2},"15":{"body":11,"breadcrumbs":3,"title":1},"16":{"body":4,"breadcrumbs":3,"title":1},"17":{"body":17,"breadcrumbs":5,"title":2},"18":{"body":34,"breadcrumbs":5,"title":2},"19":{"body":14,"breadcrumbs":2,"title":1},"2":{"body":22,"breadcrumbs":3,"title":2},"20":{"body":16,"breadcrumbs":3,"title":2},"3":{"body":6,"breadcrumbs":2,"title":1},"4":{"body":52,"breadcrumbs":3,"title":1},"5":{"body":41,"breadcrumbs":3,"title":1},"6":{"body":22,"breadcrumbs":5,"title":2},"7":{"body":50,"breadcrumbs":5,"title":2},"8":{"body":33,"breadcrumbs":5,"title":2},"9":{"body":37,"breadcrumbs":5,"title":2}},"docs":{"0":{"body":"The goal of this book is to help you understand the inner-workings of cargo-breaking and how it compares two versions of a Rust project to display the differences between both. Example: $ cargo breaking\n- user::User::from_str\n≠ user::User\n+ user::User::from_path\n+ user::User::[impl Debug] Next version is: 3.0.0","breadcrumbs":"Foreword » Foreword","id":"0","title":"Foreword"},"1":{"body":"cargo-breaking needs the nightly toolchain to be installed to work correctly, but can be compiled with any toolchain. It can be compiled from sources with the following commands: $ git clone https://github.com/iomentum/cargo-breaking\n$ cd cargo-breaking\n$ cargo install --path ./ You may need to add the --force argument to the last command if you're upgrading from a previous version.","breadcrumbs":"Foreword » Installation","id":"1","title":"Installation"},"10":{"body":"The Crate object from Rustdoc is converted to a PublicApi object, which is a flat hashmap of all public items in the library.","breadcrumbs":"Process » Comparison » Comparison","id":"10","title":"Comparison"},"11":{"body":"The Crate object is traversed and all its items associated to the root crate (ID 0) are processed.","breadcrumbs":"Process » Comparison » Translation","id":"11","title":"Translation"},"12":{"body":"Free functions and methods yield Fn and Method items, respectively.","breadcrumbs":"Process » Comparison » Functions","id":"12","title":"Functions"},"13":{"body":"Structures, unions and enums yield Type items, and their fields and impls are also traversed. Fields Fields yield Field items. Impls Impl items are processed and yield either Method, AssocType or AssocConst items. If the impl is for a trait, it yields a TraitImpl item.","breadcrumbs":"Process » Comparison » Types","id":"13","title":"Types"},"14":{"body":"Trait definitions yield TraitDef items, and their items are processed in the same way as for impl items.","breadcrumbs":"Process » Comparison » Trait definitions","id":"14","title":"Trait definitions"},"15":{"body":"Modules yield Module items. Their items are not directly processed since they already appear in the Crate index.","breadcrumbs":"Process » Comparison » Modules","id":"15","title":"Modules"},"16":{"body":"Typedefs yield Type items.","breadcrumbs":"Process » Comparison » Typedefs","id":"16","title":"Typedefs"},"17":{"body":"If the diagnosis contains breaking changes, the major version is incremented. Otherwise, if it contains additions, the minor version is incremented. Otherwise, the patch version is incremented.","breadcrumbs":"Process » Next version » Next version","id":"17","title":"Next version"},"18":{"body":"Currently, only modifications are considered breaking changes. Note: additions of public fields to a structure also yield a modification of the structure itself. The additions are not internally considered breaking, the structure's modification is. This is to avoid having to handle different kinds of additions in the comparison phase; we can simply emit a modification if we detect that the addition is breaking.","breadcrumbs":"Process » Next version » Breaking changes","id":"18","title":"Breaking changes"},"19":{"body":"Mdbook is needed to get running this book: $ cargo install mdbook\n$ cd book\n$ mdbook serve --dest-dir ../docs","breadcrumbs":"Book » Book","id":"19","title":"Book"},"2":{"body":"Most work is done in separate branches, before getting merged to main all at once, once the quality of the code is judged to be good enough. The branches usually follow the naming convention author/feature-name.","breadcrumbs":"Foreword » Git workflow","id":"2","title":"Git workflow"},"20":{"body":"This updates the book so it is updated on push. // TODO! add this as a pre-commit hook $ cd book\n$ mdbook build --dest-dir ../docs","breadcrumbs":"Book » Building the book","id":"20","title":"Building the book"},"3":{"body":"This section describes the execution flow of cargo-breaking.","breadcrumbs":"Process » Process","id":"3","title":"Process"},"4":{"body":"The process used by cargo-breaking can be summarized like this: 2.2 : The configuration is parsed from the CLI args and other configuration sources 2.3 : The crate metadata is read from the manifest file 2.4 : The source code for the previous and the next version is fetched using Git in a temporary directory 2.5 : Both codebases are ran through rustdoc and the JSON output is then deserialized 2.6 : The comparison is performed and the output is collected as a list of differences 2.7 : The \"best\" next version is suggested from the diagnosis list","breadcrumbs":"Process » Overview » Overview","id":"4","title":"Overview"},"5":{"body":"cargo-breaking supports loading configuration data from the following sources, in order of priority (highest to lowest): Command-line parameters, using clap Environment variables, with the prefix CARGO_BREAKING_ The cargo-breaking.toml file The cargo-breaking.json file Configuration files are searched for in the current working directory. The loaded configuration is exposed via cli::config's get() -> &'static ProgramConfig function.","breadcrumbs":"Process » Configuration » Configuration","id":"5","title":"Configuration"},"6":{"body":"cargo-breaking searches for the Cargo.toml file in the current working directory, or in a subdirectory if a package is specified via -p / --package. The file is then read and deserialized, and the crate's package name and version is fetched.","breadcrumbs":"Process » Manifest file » Manifest file","id":"6","title":"Manifest file"},"7":{"body":"Once the crate's metadata has been decoded, cargo-breaking fetches the code for the previous and the next version in a temporary directory. This is done by copying the .git folder in two different directories, and then checking out respectively the revspec specified via -a / --against and the HEAD revspec. cargo-breaking internally supports a second loading source, which is used for loading raw code files directly. This is used by the test harness to compare two single-file libraries.","breadcrumbs":"Process » Code fetching » Code fetching","id":"7","title":"Code fetching"},"8":{"body":"The following command is executed in each of the two codebases: cargo +nightly rustdoc --lib ... -- -Zunstable-options -wjson where ... represent additional optional Cargo arguments depending on the settings: --features --no-default-features --all-features Rustdoc writes its output in the target/doc/{package_name}.json file, which is then read and deserialized into a Crate object.","breadcrumbs":"Process » Rustdoc execution » Rustdoc execution","id":"8","title":"Rustdoc execution"},"9":{"body":"The rustdoc_types crate contain copies of the Rustdoc internal type hierarchy, directly extracted from the rustc repository. This allows reading the type without having to depend on the whole rustc codebase. However, because of the processing we are doing on the Rustdoc output, we ourselves had to make copies of those types, to make some changes in the way some things are stored, for example IDs.","breadcrumbs":"Process » Rustdoc execution » Types handling","id":"9","title":"Types handling"}},"length":21,"save":true},"fields":["title","body","breadcrumbs"],"index":{"body":{"root":{"0":{"df":1,"docs":{"11":{"tf":1.0}}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"20":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":2.0},"8":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"c":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"/":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":3,"docs":{"0":{"tf":1.0},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"17":{"tf":1.0},"18":{"tf":2.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"_":{"b":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"19":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{},"i":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"'":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"b":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":3,"docs":{"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"1":{"tf":1.4142135623730951},"5":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"0":{"tf":1.0},"7":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":3,"docs":{"10":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":2.0}}}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"17":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"7":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}},"df":6,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"15":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"18":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0}}}}},"t":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"17":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"0":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"15":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}},"df":1,"docs":{"9":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"13":{"tf":1.0}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"0":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"3":{"tf":1.0},"8":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"13":{"tf":2.0},"18":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"e":{"df":5,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"n":{"df":1,"docs":{"12":{"tf":1.0}}},"o":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"8":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"7":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":1,"docs":{"0":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"d":{"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"13":{"tf":2.0},"14":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.7320508075688772}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"1":{"tf":1.7320508075688772},"19":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":3,"docs":{"18":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":2.23606797749979},"14":{"tf":1.7320508075688772},"15":{"tf":1.4142135623730951},"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}}},"u":{"d":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"8":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"5":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}},"o":{"a":{"d":{"df":2,"docs":{"5":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.7320508075688772},"20":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"t":{"a":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":2.0}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"6":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"19":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":4,"docs":{"0":{"tf":1.0},"17":{"tf":1.0},"4":{"tf":1.4142135623730951},"7":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":3,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"7":{"tf":1.0}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.4142135623730951}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"17":{"tf":1.4142135623730951}}}}}}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"9":{"tf":1.0}}}}}}},"t":{"df":1,"docs":{"7":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"6":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"17":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":1,"docs":{"6":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"20":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"1":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}},"df":2,"docs":{"10":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"20":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"r":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}},"w":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":4,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"7":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"19":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}},"d":{"df":0,"docs":{},"o":{"c":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":4,"docs":{"10":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"0":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"5":{"tf":1.0},"6":{"tf":1.0}}}},"df":0,"docs":{}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"19":{"tf":1.0}}}},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":4,"docs":{"1":{"tf":1.0},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"18":{"tf":1.4142135623730951}},"e":{"'":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"u":{"b":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"5":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"/":{"d":{"df":0,"docs":{},"o":{"c":{"/":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"}":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"9":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"14":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"11":{"tf":1.0},"13":{"tf":1.0}}}}}}},"df":0,"docs":{}},"w":{"df":0,"docs":{},"o":{"df":3,"docs":{"0":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.4142135623730951}}}}},"df":3,"docs":{"13":{"tf":1.4142135623730951},"16":{"tf":1.0},"9":{"tf":2.0}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"[":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"17":{"tf":2.0},"4":{"tf":1.4142135623730951},"6":{"tf":1.0},"7":{"tf":1.0}}}}}}}},"i":{"a":{"df":3,"docs":{"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":6,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"breadcrumbs":{"root":{"0":{"df":1,"docs":{"11":{"tf":1.0}}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"20":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":2.0},"8":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"c":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"/":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":3,"docs":{"0":{"tf":1.0},"19":{"tf":2.23606797749979},"20":{"tf":2.23606797749979}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"17":{"tf":1.0},"18":{"tf":2.23606797749979},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"_":{"b":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"19":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":1.7320508075688772},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{},"i":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"'":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"b":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":3,"docs":{"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":2.23606797749979}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"1":{"tf":1.4142135623730951},"5":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"0":{"tf":1.0},"7":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":9,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":2.449489742783178}}}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"17":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"7":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}},"df":6,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"15":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"18":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0}}}}},"t":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"17":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"0":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"15":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}},"df":1,"docs":{"9":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"13":{"tf":1.0}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"0":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"3":{"tf":1.0},"8":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"13":{"tf":2.0},"18":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"e":{"df":5,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":2.23606797749979},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"n":{"df":1,"docs":{"12":{"tf":1.0}}},"o":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"8":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":3,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0},"2":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"7":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":1,"docs":{"0":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"d":{"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"13":{"tf":2.0},"14":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.7320508075688772}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"1":{"tf":2.0},"19":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":3,"docs":{"18":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":2.23606797749979},"14":{"tf":1.7320508075688772},"15":{"tf":1.4142135623730951},"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}}},"u":{"d":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"8":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"5":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}},"o":{"a":{"d":{"df":2,"docs":{"5":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.7320508075688772}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.7320508075688772},"20":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"t":{"a":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":2.0}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":2.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"6":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"19":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"0":{"tf":1.0},"17":{"tf":1.7320508075688772},"18":{"tf":1.0},"4":{"tf":1.4142135623730951},"7":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":3,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"7":{"tf":1.0}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.4142135623730951}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"17":{"tf":1.4142135623730951}}}}}}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"9":{"tf":1.0}}}}}}},"t":{"df":1,"docs":{"7":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.7320508075688772}}}}}}}}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"6":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"17":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":1,"docs":{"6":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"20":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"1":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":16,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"15":{"tf":1.4142135623730951},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"3":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}},"df":2,"docs":{"10":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"20":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"r":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}},"w":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":4,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"7":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"19":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}},"d":{"df":0,"docs":{},"o":{"c":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":4,"docs":{"10":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"df":1,"docs":{"0":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"5":{"tf":1.0},"6":{"tf":1.0}}}},"df":0,"docs":{}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"19":{"tf":1.0}}}},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":4,"docs":{"1":{"tf":1.0},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"18":{"tf":1.4142135623730951}},"e":{"'":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"u":{"b":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"5":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"/":{"d":{"df":0,"docs":{},"o":{"c":{"/":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"}":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"9":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"14":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"11":{"tf":1.0},"13":{"tf":1.0}}}}}}},"df":0,"docs":{}},"w":{"df":0,"docs":{},"o":{"df":3,"docs":{"0":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.7320508075688772}}}}},"df":3,"docs":{"13":{"tf":1.7320508075688772},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"[":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":7,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"17":{"tf":2.449489742783178},"18":{"tf":1.0},"4":{"tf":1.4142135623730951},"6":{"tf":1.0},"7":{"tf":1.0}}}}}}}},"i":{"a":{"df":3,"docs":{"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":6,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"title":{"root":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"10":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}},"df":0,"docs":{}}}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"6":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}}},"m":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"6":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"9":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"17":{"tf":1.0}}}}}}}}},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}}); \ No newline at end of file diff --git a/docs/searchindex.json b/docs/searchindex.json index 622c69a..c77e68e 100644 --- a/docs/searchindex.json +++ b/docs/searchindex.json @@ -1 +1 @@ -{"doc_urls":["1_foreword.html#foreword","1_foreword.html#installation","1_foreword.html#git-workflow","2_0_process.html#process","2_1_overview.html#overview","2_2_config.html#configuration","2_3_repository.html#repository","2_4_manifest.html#manifest","2_5_api_extraction.html#api-extraction","2_6_comparator.html#comparator","2_7_diagnosis.html#diagnosis","2_8_next_version.html#next-version","3_book.html#book","3_book.html#building-the-book"],"index":{"documentStore":{"docInfo":{"0":{"body":29,"breadcrumbs":2,"title":1},"1":{"body":34,"breadcrumbs":2,"title":1},"10":{"body":2,"breadcrumbs":3,"title":1},"11":{"body":2,"breadcrumbs":5,"title":2},"12":{"body":14,"breadcrumbs":2,"title":1},"13":{"body":16,"breadcrumbs":3,"title":2},"2":{"body":41,"breadcrumbs":3,"title":2},"3":{"body":6,"breadcrumbs":2,"title":1},"4":{"body":52,"breadcrumbs":3,"title":1},"5":{"body":2,"breadcrumbs":3,"title":1},"6":{"body":2,"breadcrumbs":3,"title":1},"7":{"body":2,"breadcrumbs":3,"title":1},"8":{"body":2,"breadcrumbs":5,"title":2},"9":{"body":2,"breadcrumbs":3,"title":1}},"docs":{"0":{"body":"This book's goal is to hold and maintain informations on how the innards of cargo-breaking works together to compare two versions of a library and display the differences between both. Example: $ cargo breaking\n- user::User::from_str\n≠ user::User\n+ user::User::from_path\n+ user::User: Debug Next version is: 3.0.0","breadcrumbs":"Foreword » Foreword","id":"0","title":"Foreword"},"1":{"body":"cargo-breaking needs the nightly toolchain to be installed to work correctly, but can be compiled with any toolchain. It can be compiled from sources with the following commands: $ git clone https://github.com/iomentum/cargo-breaking\n$ cd cargo-breaking\n$ cargo install --path ./ You may need to add the --force argument to the last command if you're upgrading from a previous version.","breadcrumbs":"Foreword » Installation","id":"1","title":"Installation"},"10":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Diagnosis » Diagnosis","id":"10","title":"Diagnosis"},"11":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Next Version » Next Version","id":"11","title":"Next Version"},"12":{"body":"Mdbook is needed to get running this book: $ cargo install mdbook\n$ cd book\n$ mdbook serve --dest-dir ../docs","breadcrumbs":"Book » Book","id":"12","title":"Book"},"13":{"body":"This updates the book so it is updated on push. // TODO! add this as a pre-commit hook $ cd book\n$ mdbook build --dest-dir ../docs","breadcrumbs":"Book » Building the book","id":"13","title":"Building the book"},"2":{"body":"Most work is commited in separate branch, before getting merged to main all at once, once we're satisfied with the refactoring, fixes, and features added. These branches are named scrabsha/iter-dd-mm-yy, representing the date at which the iteration is started (for instance, scrabsha/iter-19-06-21). Installing cargo-breaking from the following branches give you the latest changes. It may have instabilities, though.","breadcrumbs":"Foreword » Git workflow","id":"2","title":"Git workflow"},"3":{"body":"This section describes the flow of the cargo-breaking application.","breadcrumbs":"Process » Process","id":"3","title":"Process"},"4":{"body":"The process used by cargo-breaking can be summarized like this: 2.2 : The configuration is parsed from the cli args 2.3 : The git repository informations are created from the env 2.4 : The crate version is fetched from the manifest 2.5 : The \"current library\" and the \"target library to run against\" are collected as AST with rustc 2.6 : Both libraries are compared against each other to collect removals, additions and modifications 2.7 : The results are gathered in a diagnosis structure 2.8 : The \"best\" next version is suggested from the diagnosis","breadcrumbs":"Process » Overview » Overview","id":"4","title":"Overview"},"5":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Configuration » Configuration","id":"5","title":"Configuration"},"6":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Repository » Repository","id":"6","title":"Repository"},"7":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Manifest » Manifest","id":"7","title":"Manifest"},"8":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » API Extraction » API Extraction","id":"8","title":"API Extraction"},"9":{"body":"WORK IN PROGRESS","breadcrumbs":"Process » Comparator » Comparator","id":"9","title":"Comparator"}},"length":14,"save":true},"fields":["title","body","breadcrumbs"],"index":{"body":{"root":{"0":{"6":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"1":{"9":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"8":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"1":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"13":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"0":{"tf":1.0}}},"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.7320508075688772}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"2":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":3,"docs":{"0":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.0},"5":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"4":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"2":{"tf":1.0}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"1":{"tf":1.0},"2":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"13":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}}},"n":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0}}},"n":{"c":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"m":{"df":1,"docs":{"2":{"tf":1.0}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"12":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"11":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"o":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"13":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":2,"docs":{"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.0},"4":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}}}}},"s":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}},"c":{"df":0,"docs":{},"r":{"a":{"b":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"a":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"12":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"13":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"o":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":1,"docs":{"4":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"11":{"tf":1.0},"4":{"tf":1.4142135623730951}}}}}}}}},"w":{"df":0,"docs":{},"e":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":10,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"10":{"tf":1.0},"11":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"breadcrumbs":{"root":{"0":{"6":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"1":{"9":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"8":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"1":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"13":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"0":{"tf":1.0}}},"df":2,"docs":{"12":{"tf":2.23606797749979},"13":{"tf":2.23606797749979}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"12":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"2":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":3,"docs":{"0":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772}}}}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"2":{"tf":1.0}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"1":{"tf":1.0},"2":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":3,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0},"2":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"1":{"tf":1.0},"2":{"tf":1.4142135623730951},"4":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"13":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}}},"n":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"1":{"tf":2.0},"12":{"tf":1.0},"2":{"tf":1.0}}},"n":{"c":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.7320508075688772}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"13":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}}},"m":{"df":1,"docs":{"2":{"tf":1.0}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"4":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"12":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"11":{"tf":1.7320508075688772},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}}}}},"o":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.7320508075688772}}}}}}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"13":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"3":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"4":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.7320508075688772}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.0},"4":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}}}}},"s":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}},"c":{"df":0,"docs":{},"r":{"a":{"b":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"a":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"12":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"13":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"o":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":1,"docs":{"4":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"11":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951}}}}}}}}},"w":{"df":0,"docs":{},"e":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":10,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"10":{"tf":1.0},"11":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}}}}}}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"title":{"root":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"8":{"tf":1.0}}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"12":{"tf":1.0},"13":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}}},"m":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"11":{"tf":1.0}}}}}}}}},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}} \ No newline at end of file +{"doc_urls":["1_foreword.html#foreword","1_foreword.html#installation","1_foreword.html#git-workflow","2_0_process.html#process","2_1_overview.html#overview","2_2_config.html#configuration","2_3_manifest.html#manifest-file","2_4_crate.html#code-fetching","2_5_rustdoc.html#rustdoc-execution","2_5_rustdoc.html#types-handling","2_6_comparison.html#comparison","2_6_comparison.html#translation","2_6_comparison.html#functions","2_6_comparison.html#types","2_6_comparison.html#trait-definitions","2_6_comparison.html#modules","2_6_comparison.html#typedefs","2_7_next_version.html#next-version","2_7_next_version.html#breaking-changes","3_book.html#book","3_book.html#building-the-book"],"index":{"documentStore":{"docInfo":{"0":{"body":28,"breadcrumbs":2,"title":1},"1":{"body":34,"breadcrumbs":2,"title":1},"10":{"body":11,"breadcrumbs":3,"title":1},"11":{"body":10,"breadcrumbs":3,"title":1},"12":{"body":8,"breadcrumbs":3,"title":1},"13":{"body":28,"breadcrumbs":3,"title":1},"14":{"body":11,"breadcrumbs":4,"title":2},"15":{"body":11,"breadcrumbs":3,"title":1},"16":{"body":4,"breadcrumbs":3,"title":1},"17":{"body":17,"breadcrumbs":5,"title":2},"18":{"body":34,"breadcrumbs":5,"title":2},"19":{"body":14,"breadcrumbs":2,"title":1},"2":{"body":22,"breadcrumbs":3,"title":2},"20":{"body":16,"breadcrumbs":3,"title":2},"3":{"body":6,"breadcrumbs":2,"title":1},"4":{"body":52,"breadcrumbs":3,"title":1},"5":{"body":41,"breadcrumbs":3,"title":1},"6":{"body":22,"breadcrumbs":5,"title":2},"7":{"body":50,"breadcrumbs":5,"title":2},"8":{"body":33,"breadcrumbs":5,"title":2},"9":{"body":37,"breadcrumbs":5,"title":2}},"docs":{"0":{"body":"The goal of this book is to help you understand the inner-workings of cargo-breaking and how it compares two versions of a Rust project to display the differences between both. Example: $ cargo breaking\n- user::User::from_str\n≠ user::User\n+ user::User::from_path\n+ user::User::[impl Debug] Next version is: 3.0.0","breadcrumbs":"Foreword » Foreword","id":"0","title":"Foreword"},"1":{"body":"cargo-breaking needs the nightly toolchain to be installed to work correctly, but can be compiled with any toolchain. It can be compiled from sources with the following commands: $ git clone https://github.com/iomentum/cargo-breaking\n$ cd cargo-breaking\n$ cargo install --path ./ You may need to add the --force argument to the last command if you're upgrading from a previous version.","breadcrumbs":"Foreword » Installation","id":"1","title":"Installation"},"10":{"body":"The Crate object from Rustdoc is converted to a PublicApi object, which is a flat hashmap of all public items in the library.","breadcrumbs":"Process » Comparison » Comparison","id":"10","title":"Comparison"},"11":{"body":"The Crate object is traversed and all its items associated to the root crate (ID 0) are processed.","breadcrumbs":"Process » Comparison » Translation","id":"11","title":"Translation"},"12":{"body":"Free functions and methods yield Fn and Method items, respectively.","breadcrumbs":"Process » Comparison » Functions","id":"12","title":"Functions"},"13":{"body":"Structures, unions and enums yield Type items, and their fields and impls are also traversed. Fields Fields yield Field items. Impls Impl items are processed and yield either Method, AssocType or AssocConst items. If the impl is for a trait, it yields a TraitImpl item.","breadcrumbs":"Process » Comparison » Types","id":"13","title":"Types"},"14":{"body":"Trait definitions yield TraitDef items, and their items are processed in the same way as for impl items.","breadcrumbs":"Process » Comparison » Trait definitions","id":"14","title":"Trait definitions"},"15":{"body":"Modules yield Module items. Their items are not directly processed since they already appear in the Crate index.","breadcrumbs":"Process » Comparison » Modules","id":"15","title":"Modules"},"16":{"body":"Typedefs yield Type items.","breadcrumbs":"Process » Comparison » Typedefs","id":"16","title":"Typedefs"},"17":{"body":"If the diagnosis contains breaking changes, the major version is incremented. Otherwise, if it contains additions, the minor version is incremented. Otherwise, the patch version is incremented.","breadcrumbs":"Process » Next version » Next version","id":"17","title":"Next version"},"18":{"body":"Currently, only modifications are considered breaking changes. Note: additions of public fields to a structure also yield a modification of the structure itself. The additions are not internally considered breaking, the structure's modification is. This is to avoid having to handle different kinds of additions in the comparison phase; we can simply emit a modification if we detect that the addition is breaking.","breadcrumbs":"Process » Next version » Breaking changes","id":"18","title":"Breaking changes"},"19":{"body":"Mdbook is needed to get running this book: $ cargo install mdbook\n$ cd book\n$ mdbook serve --dest-dir ../docs","breadcrumbs":"Book » Book","id":"19","title":"Book"},"2":{"body":"Most work is done in separate branches, before getting merged to main all at once, once the quality of the code is judged to be good enough. The branches usually follow the naming convention author/feature-name.","breadcrumbs":"Foreword » Git workflow","id":"2","title":"Git workflow"},"20":{"body":"This updates the book so it is updated on push. // TODO! add this as a pre-commit hook $ cd book\n$ mdbook build --dest-dir ../docs","breadcrumbs":"Book » Building the book","id":"20","title":"Building the book"},"3":{"body":"This section describes the execution flow of cargo-breaking.","breadcrumbs":"Process » Process","id":"3","title":"Process"},"4":{"body":"The process used by cargo-breaking can be summarized like this: 2.2 : The configuration is parsed from the CLI args and other configuration sources 2.3 : The crate metadata is read from the manifest file 2.4 : The source code for the previous and the next version is fetched using Git in a temporary directory 2.5 : Both codebases are ran through rustdoc and the JSON output is then deserialized 2.6 : The comparison is performed and the output is collected as a list of differences 2.7 : The \"best\" next version is suggested from the diagnosis list","breadcrumbs":"Process » Overview » Overview","id":"4","title":"Overview"},"5":{"body":"cargo-breaking supports loading configuration data from the following sources, in order of priority (highest to lowest): Command-line parameters, using clap Environment variables, with the prefix CARGO_BREAKING_ The cargo-breaking.toml file The cargo-breaking.json file Configuration files are searched for in the current working directory. The loaded configuration is exposed via cli::config's get() -> &'static ProgramConfig function.","breadcrumbs":"Process » Configuration » Configuration","id":"5","title":"Configuration"},"6":{"body":"cargo-breaking searches for the Cargo.toml file in the current working directory, or in a subdirectory if a package is specified via -p / --package. The file is then read and deserialized, and the crate's package name and version is fetched.","breadcrumbs":"Process » Manifest file » Manifest file","id":"6","title":"Manifest file"},"7":{"body":"Once the crate's metadata has been decoded, cargo-breaking fetches the code for the previous and the next version in a temporary directory. This is done by copying the .git folder in two different directories, and then checking out respectively the revspec specified via -a / --against and the HEAD revspec. cargo-breaking internally supports a second loading source, which is used for loading raw code files directly. This is used by the test harness to compare two single-file libraries.","breadcrumbs":"Process » Code fetching » Code fetching","id":"7","title":"Code fetching"},"8":{"body":"The following command is executed in each of the two codebases: cargo +nightly rustdoc --lib ... -- -Zunstable-options -wjson where ... represent additional optional Cargo arguments depending on the settings: --features --no-default-features --all-features Rustdoc writes its output in the target/doc/{package_name}.json file, which is then read and deserialized into a Crate object.","breadcrumbs":"Process » Rustdoc execution » Rustdoc execution","id":"8","title":"Rustdoc execution"},"9":{"body":"The rustdoc_types crate contain copies of the Rustdoc internal type hierarchy, directly extracted from the rustc repository. This allows reading the type without having to depend on the whole rustc codebase. However, because of the processing we are doing on the Rustdoc output, we ourselves had to make copies of those types, to make some changes in the way some things are stored, for example IDs.","breadcrumbs":"Process » Rustdoc execution » Types handling","id":"9","title":"Types handling"}},"length":21,"save":true},"fields":["title","body","breadcrumbs"],"index":{"body":{"root":{"0":{"df":1,"docs":{"11":{"tf":1.0}}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"20":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":2.0},"8":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"c":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"/":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":3,"docs":{"0":{"tf":1.0},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"17":{"tf":1.0},"18":{"tf":2.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"_":{"b":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"19":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{},"i":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"'":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"b":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":3,"docs":{"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"1":{"tf":1.4142135623730951},"5":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"0":{"tf":1.0},"7":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":3,"docs":{"10":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":2.0}}}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"17":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"7":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}},"df":6,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"15":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"18":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0}}}}},"t":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"17":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"0":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"15":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}},"df":1,"docs":{"9":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"13":{"tf":1.0}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"0":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"3":{"tf":1.0},"8":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"13":{"tf":2.0},"18":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"e":{"df":5,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"n":{"df":1,"docs":{"12":{"tf":1.0}}},"o":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"8":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"7":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":1,"docs":{"0":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"d":{"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"13":{"tf":2.0},"14":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.7320508075688772}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"1":{"tf":1.7320508075688772},"19":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":3,"docs":{"18":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":2.23606797749979},"14":{"tf":1.7320508075688772},"15":{"tf":1.4142135623730951},"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}}},"u":{"d":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"8":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"5":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}},"o":{"a":{"d":{"df":2,"docs":{"5":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.7320508075688772},"20":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"t":{"a":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":2.0}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"6":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"19":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":4,"docs":{"0":{"tf":1.0},"17":{"tf":1.0},"4":{"tf":1.4142135623730951},"7":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":3,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"7":{"tf":1.0}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.4142135623730951}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"17":{"tf":1.4142135623730951}}}}}}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"9":{"tf":1.0}}}}}}},"t":{"df":1,"docs":{"7":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"6":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"17":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":1,"docs":{"6":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"20":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"1":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}},"df":2,"docs":{"10":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"20":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"r":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}},"w":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":4,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"7":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"19":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}},"d":{"df":0,"docs":{},"o":{"c":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":4,"docs":{"10":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"0":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"5":{"tf":1.0},"6":{"tf":1.0}}}},"df":0,"docs":{}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"19":{"tf":1.0}}}},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":4,"docs":{"1":{"tf":1.0},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"18":{"tf":1.4142135623730951}},"e":{"'":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"u":{"b":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"5":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"/":{"d":{"df":0,"docs":{},"o":{"c":{"/":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"}":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"9":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"14":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"11":{"tf":1.0},"13":{"tf":1.0}}}}}}},"df":0,"docs":{}},"w":{"df":0,"docs":{},"o":{"df":3,"docs":{"0":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.4142135623730951}}}}},"df":3,"docs":{"13":{"tf":1.4142135623730951},"16":{"tf":1.0},"9":{"tf":2.0}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"[":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"17":{"tf":2.0},"4":{"tf":1.4142135623730951},"6":{"tf":1.0},"7":{"tf":1.0}}}}}}}},"i":{"a":{"df":3,"docs":{"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":6,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"breadcrumbs":{"root":{"0":{"df":1,"docs":{"11":{"tf":1.0}}},"2":{".":{"2":{"df":1,"docs":{"4":{"tf":1.0}}},"3":{"df":1,"docs":{"4":{"tf":1.0}}},"4":{"df":1,"docs":{"4":{"tf":1.0}}},"5":{"df":1,"docs":{"4":{"tf":1.0}}},"6":{"df":1,"docs":{"4":{"tf":1.0}}},"7":{"df":1,"docs":{"4":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{".":{"0":{".":{"0":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"d":{"d":{"df":2,"docs":{"1":{"tf":1.0},"20":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":2.0},"8":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"15":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"4":{"tf":1.0}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"c":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"/":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"0":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":3,"docs":{"0":{"tf":1.0},"19":{"tf":2.23606797749979},"20":{"tf":2.23606797749979}}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"0":{"tf":1.0},"4":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"17":{"tf":1.0},"18":{"tf":2.23606797749979},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}},"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"l":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"_":{"b":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":9,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"19":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}}}},"d":{"df":3,"docs":{"1":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"17":{"tf":1.0},"18":{"tf":1.7320508075688772},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{},"i":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"'":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"4":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"b":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":3,"docs":{"2":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":2.23606797749979}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"1":{"tf":1.4142135623730951},"5":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"0":{"tf":1.0},"7":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":9,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":2.449489742783178}}}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"18":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"17":{"tf":1.4142135623730951},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"7":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}},"df":6,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"15":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"18":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}}}}}}}}},"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"0":{"tf":1.0}}}}},"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0}}}}},"t":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":2,"docs":{"17":{"tf":1.0},"4":{"tf":1.0}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"0":{"tf":1.0},"18":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}},"r":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"15":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"4":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"c":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}},"df":1,"docs":{"9":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"18":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"2":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"13":{"tf":1.0}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"0":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"3":{"tf":1.0},"8":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"8":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"13":{"tf":2.0},"18":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"e":{"df":5,"docs":{"4":{"tf":1.0},"5":{"tf":1.7320508075688772},"6":{"tf":2.23606797749979},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"3":{"tf":1.0}}}}},"n":{"df":1,"docs":{"12":{"tf":1.0}}},"o":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"8":{"tf":1.0}}}}}},"r":{"c":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":3,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0},"2":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"12":{"tf":1.7320508075688772},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"1":{"tf":1.0},"2":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0}}}},"o":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"2":{"tf":1.0}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"7":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"10":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"18":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":1,"docs":{"0":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{":":{"/":{"/":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"u":{"b":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"/":{"c":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"i":{"d":{"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"13":{"tf":2.0},"14":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.7320508075688772}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"1":{"tf":2.0},"19":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":3,"docs":{"18":{"tf":1.0},"7":{"tf":1.0},"9":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":7,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":2.23606797749979},"14":{"tf":1.7320508075688772},"15":{"tf":1.4142135623730951},"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}}},"u":{"d":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"8":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"10":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":1,"docs":{"5":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.4142135623730951}}}}},"o":{"a":{"d":{"df":2,"docs":{"5":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"2":{"tf":1.0}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"6":{"tf":1.7320508075688772}}}}}}}}},"d":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.7320508075688772},"20":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"2":{"tf":1.0}}}},"t":{"a":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"13":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"18":{"tf":2.0}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":2.0}}}}},"df":0,"docs":{}}},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"6":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"d":{"df":2,"docs":{"1":{"tf":1.4142135623730951},"19":{"tf":1.0}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"0":{"tf":1.0},"17":{"tf":1.7320508075688772},"18":{"tf":1.0},"4":{"tf":1.4142135623730951},"7":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"1":{"tf":1.0},"8":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":3,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"2":{"tf":1.4142135623730951},"7":{"tf":1.0}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.4142135623730951}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"17":{"tf":1.4142135623730951}}}}}}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"9":{"tf":1.0}}}}}}},"t":{"df":1,"docs":{"7":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.0}}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.7320508075688772}}}}}}}}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"6":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"5":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":1,"docs":{"4":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"17":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":1,"docs":{"6":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"20":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"1":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":16,"docs":{"10":{"tf":1.0},"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"15":{"tf":1.4142135623730951},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"3":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"10":{"tf":1.0}}}}},"df":2,"docs":{"10":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"20":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"2":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"r":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}},"w":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"e":{"a":{"d":{"df":4,"docs":{"4":{"tf":1.0},"6":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"12":{"tf":1.0},"7":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"c":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"19":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"c":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}},"d":{"df":0,"docs":{},"o":{"c":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":4,"docs":{"10":{"tf":1.0},"4":{"tf":1.0},"8":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"df":1,"docs":{"0":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"5":{"tf":1.0},"6":{"tf":1.0}}}},"df":0,"docs":{}}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"v":{"df":1,"docs":{"19":{"tf":1.0}}}},"t":{"df":1,"docs":{"8":{"tf":1.0}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"18":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":4,"docs":{"1":{"tf":1.0},"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":2,"docs":{"6":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"18":{"tf":1.4142135623730951}},"e":{"'":{"df":1,"docs":{"18":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"u":{"b":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"6":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"4":{"tf":1.0}}}}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"5":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"/":{"d":{"df":0,"docs":{},"o":{"c":{"/":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"}":{".":{"df":0,"docs":{},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"9":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}},"o":{"d":{"df":0,"docs":{},"o":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"14":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"11":{"tf":1.0},"13":{"tf":1.0}}}}}}},"df":0,"docs":{}},"w":{"df":0,"docs":{},"o":{"df":3,"docs":{"0":{"tf":1.0},"7":{"tf":1.4142135623730951},"8":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.7320508075688772}}}}},"df":3,"docs":{"13":{"tf":1.7320508075688772},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"d":{"df":1,"docs":{"1":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"s":{"df":3,"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":1.0},"7":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{":":{":":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{":":{":":{"[":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"0":{"tf":1.0}}}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"0":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"2":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":7,"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"17":{"tf":2.449489742783178},"18":{"tf":1.0},"4":{"tf":1.4142135623730951},"6":{"tf":1.0},"7":{"tf":1.0}}}}}}}},"i":{"a":{"df":3,"docs":{"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}},"j":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"2":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0}},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.4142135623730951}}}}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"8":{"tf":1.0}}}}}}},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":6,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"18":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"r":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"8":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"title":{"root":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"df":2,"docs":{"19":{"tf":1.0},"20":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"18":{"tf":1.0}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"18":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"10":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"8":{"tf":1.0}}}}},"df":0,"docs":{}}}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"6":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"0":{"tf":1.0}}},"df":0,"docs":{}}}}}}},"u":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"2":{"tf":1.0}}}}},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"9":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"1":{"tf":1.0}}}},"df":0,"docs":{}}}}},"m":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"6":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"15":{"tf":1.0}}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"17":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"4":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"11":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":2,"docs":{"13":{"tf":1.0},"9":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"17":{"tf":1.0}}}}}}}}},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"2":{"tf":1.0}}}}}}}}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}} \ No newline at end of file diff --git a/docs/tomorrow-night.css b/docs/tomorrow-night.css index f719792..5b4aca7 100644 --- a/docs/tomorrow-night.css +++ b/docs/tomorrow-night.css @@ -81,8 +81,6 @@ overflow-x: auto; background: #1d1f21; color: #c5c8c6; - padding: 0.5em; - -webkit-text-size-adjust: none; } .coffeescript .javascript, From cc486e306e942cabda56187399d4fa5824f9c3af Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Mon, 11 Jul 2022 18:24:13 +0200 Subject: [PATCH 4/4] Better specify major/minor changes --- src/comparator.rs | 17 +++++++++++++++++ src/diagnosis.rs | 8 ++++++++ src/lib.rs | 2 ++ src/public_api.rs | 12 ++++++++---- src/public_api/types.rs | 24 +++++++++++++++++++----- tests/struct.rs | 14 +++++++++++++- 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/comparator.rs b/src/comparator.rs index 5bcec35..3ce17e6 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -68,6 +68,13 @@ impl Display for ApiCompatibilityDiagnostics { } } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum DiagnosticVersionChange { + Major, + Minor, + Patch, +} + impl ApiCompatibilityDiagnostics { pub fn is_empty(&self) -> bool { self.diags.is_empty() @@ -130,6 +137,16 @@ impl ApiCompatibilityDiagnostics { fn next_patch(v: &mut Version) { v.patch += 1; } + + pub fn get_version_change(&self) -> DiagnosticVersionChange { + if self.contains_breaking_changes() { + DiagnosticVersionChange::Major + } else if self.contains_additions() { + DiagnosticVersionChange::Minor + } else { + DiagnosticVersionChange::Patch + } + } } pub(crate) fn map_difference<'a, K, V>( diff --git a/src/diagnosis.rs b/src/diagnosis.rs index ff7c0bb..ecb3f5e 100644 --- a/src/diagnosis.rs +++ b/src/diagnosis.rs @@ -109,6 +109,14 @@ impl DiagnosisItem { self.breaking } + pub(crate) fn set_breaking(self, breaking: bool) -> DiagnosisItem { + DiagnosisItem { + kind: self.kind, + path: self.path, + breaking, + } + } + pub(crate) fn is_addition(&self) -> bool { self.kind == DiagnosisItemKind::Addition } diff --git a/src/lib.rs b/src/lib.rs index c116af8..4298e9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ mod manifest; mod public_api; mod rustdoc; +pub use comparator::DiagnosticVersionChange; + use anyhow::{Context, Result as AnyResult}; use clap::{crate_name, crate_version}; pub use comparator::ApiCompatibilityDiagnostics; diff --git a/src/public_api.rs b/src/public_api.rs index 0b89cb1..84a5593 100644 --- a/src/public_api.rs +++ b/src/public_api.rs @@ -69,17 +69,17 @@ impl PublicApi { let kind = match &item.inner { ItemEnum::Function(f) => ItemKindData::Fn(f.to_cb(data)), ItemEnum::Struct(s) => { - Self::process_fields(data, res, path, &s.fields)?; + Self::process_fields(data, res, path, &s.fields, s.fields_stripped)?; Self::process_impls(data, res, path, &s.impls)?; ItemKindData::Type(s.to_cb(data)) } ItemEnum::Union(u) => { - Self::process_fields(data, res, path, &u.fields)?; + Self::process_fields(data, res, path, &u.fields, u.fields_stripped)?; Self::process_impls(data, res, path, &u.impls)?; ItemKindData::Type(u.to_cb(data)) } ItemEnum::Enum(e) => { - Self::process_variants(data, res, path, &e.variants)?; + Self::process_variants(data, res, path, &e.variants, e.variants_stripped)?; Self::process_impls(data, res, path, &e.impls)?; ItemKindData::Type(e.to_cb(data)) } @@ -231,6 +231,7 @@ impl PublicApi { res: &mut Vec<(ItemPath, ItemKind)>, path: &ItemPath, variants: &[Id], + parent_stripped: bool, ) -> AnyResult<()> { for v in variants { let var_data = Self::find_item(data, v)?; @@ -253,13 +254,14 @@ impl PublicApi { data: ItemKindData::Field(TypeFieldMetadata::new( i.to_string(), field.to_cb(data), + parent_stripped, )), }, )); } } Variant::Struct(f) => { - Self::process_fields(data, res, &new_path, f)?; + Self::process_fields(data, res, &new_path, f, parent_stripped)?; } }, _ => bail!("Unexpected item type in enum: {:?}", var_data.inner), @@ -278,6 +280,7 @@ impl PublicApi { res: &mut Vec<(ItemPath, ItemKind)>, path: &ItemPath, fields: &[Id], + parent_stripped: bool, ) -> AnyResult<()> { for f in fields { let field_data = Self::find_item(data, f)?; @@ -297,6 +300,7 @@ impl PublicApi { field_data.inner ), }, + parent_stripped, )), deprecated: field_data.deprecation.is_some(), }, diff --git a/src/public_api/types.rs b/src/public_api/types.rs index a994f24..5a44d29 100644 --- a/src/public_api/types.rs +++ b/src/public_api/types.rs @@ -1,6 +1,8 @@ +use derivative::Derivative; use rustdoc_types::{Crate, Enum, Struct, StructType, Typedef, Union}; -use crate::diagnosis::DiagnosticGenerator; +use crate::diagnosis::{DiagnosisCollector, DiagnosisItem, DiagnosticGenerator}; +use crate::public_api::ItemPath; use crate::rustdoc::types::{Generics, RustdocToCb, Type}; @@ -74,19 +76,31 @@ impl InnerTypeMetadata { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Derivative)] +#[derivative(PartialEq)] pub(crate) struct TypeFieldMetadata { name: String, ty: Type, + #[derivative(PartialEq = "ignore")] + parent_stripped: bool, } impl TypeFieldMetadata { - pub fn new(name: String, ty: Type) -> TypeFieldMetadata { - TypeFieldMetadata { name, ty } + pub fn new(name: String, ty: Type, parent_stripped: bool) -> TypeFieldMetadata { + TypeFieldMetadata { + name, + ty, + parent_stripped, + } } } -impl DiagnosticGenerator for TypeFieldMetadata {} +impl DiagnosticGenerator for TypeFieldMetadata { + fn addition_diagnosis(&self, path: &ItemPath, collector: &mut DiagnosisCollector) { + // adding a (public) field is breaking if the parent type doesn't have private fields + collector.add(DiagnosisItem::addition(path.clone()).set_breaking(!self.parent_stripped)); + } +} #[derive(Clone, Debug, PartialEq)] pub(crate) struct EnumVariantMetadata { diff --git a/tests/struct.rs b/tests/struct.rs index 61f2664..efce3fe 100644 --- a/tests/struct.rs +++ b/tests/struct.rs @@ -1,4 +1,5 @@ use cargo_breaking::tests::get_diff; +use cargo_breaking::DiagnosticVersionChange::*; #[test] fn private_is_not_reported() { @@ -22,6 +23,7 @@ fn addition() { }; assert_eq!(diff.to_string(), "+ A (struct)\n"); + assert_eq!(diff.get_version_change(), Minor); } #[test] @@ -34,6 +36,7 @@ fn removal() { }; assert_eq!(diff.to_string(), "- B (struct)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -48,6 +51,7 @@ fn new_public_field_tupled_is_modification() { }; assert_eq!(diff.to_string(), "≠ C (struct)\n+ C::0 (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -62,6 +66,7 @@ fn new_private_field_tupled_is_modification() { }; assert_eq!(diff.to_string(), "≠ C (struct)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -92,6 +97,7 @@ fn new_public_field_named_is_modification() { }; assert_eq!(diff.to_string(), "+ D::a (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -130,6 +136,7 @@ fn new_private_field_named_with_existing_public_is_modification() { }; assert_eq!(diff.to_string(), "≠ D (struct)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -167,6 +174,7 @@ fn public_named_field_modification() { }; assert_eq!(diff.to_string(), "≠ A::a (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -181,6 +189,7 @@ fn public_unnamed_field_modification() { }; assert_eq!(diff.to_string(), "≠ A::0 (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -197,6 +206,7 @@ fn public_named_field_removal_is_modification() { }; assert_eq!(diff.to_string(), "- A::a (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] @@ -211,13 +221,14 @@ fn public_unnamed_field_removal_is_modification() { }; assert_eq!(diff.to_string(), "- A::0 (struct field)\n"); + assert_eq!(diff.get_version_change(), Major); } #[test] fn generic_change_is_modification() { let diff = get_diff! { { - pub struct E; + pub struct E { f: u8 } }, { pub struct E { f: T } @@ -241,4 +252,5 @@ fn whole_struct_removal() { }; assert_eq!(diff.to_string(), "- A (struct)\n"); + assert_eq!(diff.get_version_change(), Major); }