[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: dtolnay\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n  schedule: [cron: \"40 1 * * *\"]\n\npermissions:\n  contents: read\n\nenv:\n  RUSTFLAGS: -Dwarnings\n\njobs:\n  pre_ci:\n    uses: dtolnay/.github/.github/workflows/pre_ci.yml@master\n\n  test:\n    name: Rust ${{matrix.rust}}\n    needs: pre_ci\n    if: needs.pre_ci.outputs.continue\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        rust: [nightly, beta, stable, 1.88.0]\n    timeout-minutes: 45\n    steps:\n      - uses: actions/checkout@v6\n      - uses: dtolnay/rust-toolchain@master\n        with:\n          toolchain: ${{matrix.rust}}\n      - name: Enable type layout randomization\n        run: echo RUSTFLAGS=${RUSTFLAGS}\\ -Zrandomize-layout >> $GITHUB_ENV\n        if: matrix.rust == 'nightly'\n      - run: cargo check\n      - run: cargo test\n      - uses: actions/upload-artifact@v6\n        if: matrix.rust == 'nightly' && always()\n        with:\n          name: Cargo.lock\n          path: Cargo.lock\n        continue-on-error: true\n\n  windows:\n    name: Windows\n    needs: pre_ci\n    if: needs.pre_ci.outputs.continue\n    runs-on: windows-latest\n    timeout-minutes: 45\n    steps:\n      - uses: actions/checkout@v6\n      - uses: dtolnay/rust-toolchain@stable\n      - run: cargo check\n\n  clippy:\n    name: Clippy\n    runs-on: ubuntu-latest\n    if: github.event_name != 'pull_request'\n    timeout-minutes: 45\n    steps:\n      - uses: actions/checkout@v6\n      - uses: dtolnay/rust-toolchain@clippy\n      - run: cargo clippy -- -Dclippy::all -Dclippy::pedantic\n\n  outdated:\n    name: Outdated\n    runs-on: ubuntu-latest\n    if: github.event_name != 'pull_request'\n    timeout-minutes: 45\n    steps:\n      - uses: actions/checkout@v6\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: dtolnay/install@cargo-outdated\n      - run: cargo outdated --workspace --exit-code 1\n"
  },
  {
    "path": ".github/workflows/install.yml",
    "content": "name: Install\n\non:\n  workflow_dispatch:\n  schedule: [cron: \"40 1 * * *\"]\n  push: {tags: ['*']}\n\npermissions: {}\n\nenv:\n  RUSTFLAGS: -Dwarnings\n\njobs:\n  install:\n    name: Install\n    uses: dtolnay/.github/.github/workflows/check_install.yml@master\n    with:\n      crate: cargo-tally\n"
  },
  {
    "path": ".gitignore",
    "content": "/*.tar.gz\n/Cargo.lock\n/dataflow-graph/\n/report.txt\n/target/\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"cargo-tally\"\nversion = \"1.0.73\"\nauthors = [\"David Tolnay <dtolnay@gmail.com>\"]\ncategories = [\"development-tools::cargo-plugins\"]\ndescription = \"Cargo subcommand for drawing graphs of the number of dependencies on a crate over time\"\nedition = \"2021\"\nkeywords = [\"cargo\", \"subcommand\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/dtolnay/cargo-tally\"\nrust-version = \"1.88\"\n\n[lib]\npath = \"src/hidden.rs\"\n\n[dependencies]\nabomonation = \"0.7\"\nanyhow = \"1.0.79\"\natomic-take = \"1.0\"\nbytesize = \"2\"\ncargo-subcommand-metadata = \"0.1\"\nchrono = \"0.4.35\"\nclap = { version = \"4\", features = [\"deprecated\"] }\ndb-dump = \"0.7.15\"\ndifferential-dataflow-master = { version = \"=0.13.0-dev.1\", default-features = false }\nfoldhash = \"0.2\"\nminipre = \"0.2\"\nnum_cpus = \"1.0\"\nopener = \"0.8\"\nref-cast = \"1.0\"\nregex = { version = \"1.9.2\", default-features = false, features = [\"perf\", \"std\"] }\nsemver = \"1.0\"\nserde = { package = \"serde_core\", version = \"1.0.220\" }\nsysinfo = { version = \"0.38\", default-features = false, features = [\"system\"] }\ntermcolor = \"1.1\"\nthiserror = \"2\"\ntimely-master = { version = \"=0.13.0-dev.1\", default-features = false }\ntyped-arena = \"2.0\"\n\n[package.metadata.docs.rs]\ntargets = [\"x86_64-unknown-linux-gnu\"]\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Cargo tally\n\n<img alt=\"Number of crates that depend directly on each regex version\" src=\"https://user-images.githubusercontent.com/1940490/122184090-bc75d600-ce40-11eb-856b-affc568d2e15.png\" width=\"30%\"> <img alt=\"Fraction of crates that depend on failure vs anyhow and thiserror\" src=\"https://user-images.githubusercontent.com/1940490/122184103-bf70c680-ce40-11eb-890c-988cd96f4428.png\" width=\"30%\"> <img alt=\"Fraction of crates.io that depends transitively on libc\" src=\"https://github.com/user-attachments/assets/712804c7-f5de-4f99-9cb2-214665c0586f\" width=\"30%\">\n\n**`cargo tally` is a Cargo subcommand for drawing graphs of the number of crates\nthat depend directly or indirectly on a crate over time.**\n\n```\nUsage: cargo tally [options] queries...\n\nOptions:\n    --db <PATH>       Path to crates.io's database dump [default: ./db-dump.tar.gz]\n    --jobs, -j <N>    Number of threads to run differential dataflow\n    --relative        Display as a fraction of total crates, not absolute number\n    --transitive      Count transitive dependencies, not just direct dependencies\n```\n\n[<img alt=\"github\" src=\"https://img.shields.io/badge/github-dtolnay/cargo--tally-8da0cb?style=for-the-badge&labelColor=555555&logo=github\" height=\"20\">](https://github.com/dtolnay/cargo-tally)\n[<img alt=\"crates.io\" src=\"https://img.shields.io/crates/v/cargo-tally.svg?style=for-the-badge&color=fc8d62&logo=rust\" height=\"20\">](https://crates.io/crates/cargo-tally)\n[<img alt=\"build status\" src=\"https://img.shields.io/github/actions/workflow/status/dtolnay/cargo-tally/ci.yml?branch=master&style=for-the-badge\" height=\"20\">](https://github.com/dtolnay/cargo-tally/actions?query=branch%3Amaster)\n\n<br>\n\n## Installation\n\n```console\n$ wget https://static.crates.io/db-dump.tar.gz\n$ cargo install cargo-tally\n```\n\n- Data is drawn from crates.io database dumps, which are published nightly by\n  automation running on crates.io. You can download a new dump whenever you feel\n  like having fresh data.\n\n- The tally command accepts a list of which crates to tally. This can either be\n  the name of a crate like `serde` or a name with arbitrary semver version\n  specification like `serde:1.0`. If a version is not specified, dependencies on\n  all versions of the crate are tallied together.\n\n- The generated graphs use [D3](https://d3js.org/); the cargo tally command\n  should pop open a browser showing your graph. It uses the same mechanism that\n  `cargo doc --open` uses so hopefully it works well on various systems.\n\n---\n\n<br>\n\n## Examples\n\n- Number of crates that depend directly on each major version of the regex\n  crate.\n\n  **`$ cargo tally regex:0.1 regex:0.2 regex:1.0`**\n\n![Number of crates that depend directly on each major version of regex][regex]\n\n---\n\n<br>\n\n- Fraction of crates.io that depends directly on each major version of the regex\n  crate. This is the same graph as the previous, but scaled to the exponentially\n  growing total number of crates on crates.io.\n\n\n  **`$ cargo tally regex:0.1 regex:0.2 regex:1.0 --relative`**\n\n![Fraction of crates.io that depends directly on each major version of regex][regex-relative]\n\n---\n\n<br>\n\n- Fraction of crates.io that depends directly on various error handling\n  libraries. Note that crates are not double-counted; a crate that depends on\n  *both* `anyhow` and `thiserror` counts as only one for the purpose of the\n  `anyhow+thiserror` curve.\n\n  **`$ cargo tally --relative quick-error error-chain failure anyhow+thiserror snafu eyre+color-eyre`**\n\n![Fraction of crates.io that depends directly on various error handling libraries][failure-anyhow-thiserror]\n\n---\n\n<br>\n\n- Fraction of crates.io that depends transitively on libc.\n\n  **`$ cargo tally --relative --transitive libc`**\n\n![Fraction of crates.io that depends transitively on libc][libc]\n\n[regex]: https://github.com/user-attachments/assets/4595d8a3-5c10-4fc2-9e38-d3ec47389257\n[regex-relative]: https://github.com/user-attachments/assets/9ecafff0-ba5b-4fec-8a75-e84ba4cd54d1\n[failure-anyhow-thiserror]: https://github.com/user-attachments/assets/885fd931-7eff-48c5-83f2-93c8b149860f\n[libc]: https://github.com/user-attachments/assets/712804c7-f5de-4f99-9cb2-214665c0586f\n\n---\n\n<br>\n\n## Credits\n\nThe implementation is powered by [differential-dataflow].\n\n<img src=\"https://raw.github.com/dtolnay/cargo-tally/72612d2290b0ab564fdc6e332bb69f556e1bb41b/ddshow.svg\">\n\n[differential-dataflow]: https://github.com/TimelyDataflow/differential-dataflow\n\n<br>\n\n#### License\n\n<sup>\nLicensed under either of <a href=\"LICENSE-APACHE\">Apache License, Version\n2.0</a> or <a href=\"LICENSE-MIT\">MIT license</a> at your option.\n</sup>\n\n<br>\n\n<sub>\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in this project by you, as defined in the Apache-2.0 license,\nshall be dual licensed as above, without any additional terms or conditions.\n</sub>\n"
  },
  {
    "path": "build.rs",
    "content": "use std::env;\nuse std::fs;\nuse std::path::Path;\nuse std::process;\n\nconst CARGO_TALLY_MEMORY_LIMIT: &str = \"CARGO_TALLY_MEMORY_LIMIT\";\n\nfn main() {\n    let limit = if let Some(value) = env::var_os(CARGO_TALLY_MEMORY_LIMIT) {\n        let Some(value) = value.to_str() else {\n            eprintln!(\"failed to parse ${CARGO_TALLY_MEMORY_LIMIT}\");\n            process::exit(1);\n        };\n        let value = match value.parse::<u64>() {\n            Ok(int) => int,\n            Err(err) => {\n                eprintln!(\"failed to parse ${CARGO_TALLY_MEMORY_LIMIT}: {err}\");\n                process::exit(1);\n            }\n        };\n        Some(value)\n    } else {\n        None\n    };\n\n    let out_dir = env::var_os(\"OUT_DIR\").unwrap();\n    let out = Path::new(&out_dir).join(\"limit.mem\");\n    fs::write(out, format!(\"{limit:?}\\n\")).unwrap();\n\n    let host = env::var_os(\"HOST\").unwrap();\n    if let Some(\"windows\") = host.to_str().unwrap().split('-').nth(2) {\n        println!(\"cargo:rustc-cfg=host_os=\\\"windows\\\"\");\n    }\n\n    println!(\"cargo:rerun-if-env-changed={CARGO_TALLY_MEMORY_LIMIT}\");\n    println!(\"cargo:rustc-check-cfg=cfg(host_os, values(\\\"windows\\\"))\");\n}\n"
  },
  {
    "path": "src/alloc.rs",
    "content": "use bytesize::ByteSize;\nuse std::alloc::{self, GlobalAlloc, Layout, System};\nuse std::fmt::{self, Display};\nuse std::ptr;\nuse std::sync::atomic::{AtomicU64, Ordering};\n\nstruct Allocator<A = System> {\n    alloc: A,\n    count: AtomicU64,\n    total: AtomicU64,\n    current: AtomicU64,\n    peak: AtomicU64,\n}\n\n#[global_allocator]\nstatic ALLOC: Allocator = Allocator {\n    alloc: System,\n    count: AtomicU64::new(0),\n    total: AtomicU64::new(0),\n    current: AtomicU64::new(0),\n    peak: AtomicU64::new(0),\n};\n\n#[cfg(not(host_os = \"windows\"))]\nconst LIMIT: Option<u64> = include!(concat!(env!(\"OUT_DIR\"), \"/limit.mem\"));\n\n#[cfg(host_os = \"windows\")]\nconst LIMIT: Option<u64> = include!(concat!(env!(\"OUT_DIR\"), \"\\\\limit.mem\"));\n\nunsafe impl<A> GlobalAlloc for Allocator<A>\nwhere\n    A: GlobalAlloc,\n{\n    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n        self.count.fetch_add(1, Ordering::Relaxed);\n\n        let size = layout.size() as u64;\n        let prev = self.current.fetch_add(size, Ordering::Relaxed);\n        self.total.fetch_add(size, Ordering::Relaxed);\n        let peak = self\n            .peak\n            .fetch_max(prev + size, Ordering::Relaxed)\n            .max(prev + size);\n\n        if let Some(limit) = LIMIT {\n            if peak > limit {\n                alloc::handle_alloc_error(layout);\n            }\n        }\n\n        unsafe { self.alloc.alloc(layout) }\n    }\n\n    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {\n        unsafe { self.alloc.dealloc(ptr, layout) };\n\n        let size = layout.size() as u64;\n        self.current.fetch_sub(size, Ordering::Relaxed);\n    }\n\n    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {\n        self.count.fetch_add(1, Ordering::Relaxed);\n\n        let size = layout.size() as u64;\n        let prev = self.current.fetch_add(size, Ordering::Relaxed);\n        self.total.fetch_add(size, Ordering::Relaxed);\n        let peak = self\n            .peak\n            .fetch_max(prev + size, Ordering::Relaxed)\n            .max(prev + size);\n\n        if let Some(limit) = LIMIT {\n            if peak > limit {\n                alloc::handle_alloc_error(layout);\n            }\n        }\n\n        unsafe { self.alloc.alloc_zeroed(layout) }\n    }\n\n    unsafe fn realloc(&self, ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 {\n        self.count.fetch_add(1, Ordering::Relaxed);\n\n        let align = old_layout.align();\n        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, align) };\n\n        let new_ptr = unsafe { self.alloc.realloc(ptr, old_layout, new_size) };\n        let old_size = old_layout.size() as u64;\n        let new_size = new_size as u64;\n\n        let peak = if ptr::eq(new_ptr, ptr) {\n            if new_size > old_size {\n                self.total.fetch_add(new_size - old_size, Ordering::Relaxed);\n                let prev = self\n                    .current\n                    .fetch_add(new_size - old_size, Ordering::Relaxed);\n                self.peak\n                    .fetch_max(prev + new_size - old_size, Ordering::Relaxed)\n                    .max(prev + new_size - old_size)\n            } else {\n                self.current\n                    .fetch_sub(old_size - new_size, Ordering::Relaxed);\n                0\n            }\n        } else {\n            self.total.fetch_add(new_size, Ordering::Relaxed);\n            let prev = if new_size > old_size {\n                self.current\n                    .fetch_add(new_size - old_size, Ordering::Relaxed)\n            } else {\n                self.current\n                    .fetch_sub(old_size - new_size, Ordering::Relaxed)\n            };\n            self.peak\n                .fetch_max(prev + new_size, Ordering::Relaxed)\n                .max(prev + new_size)\n        };\n\n        if let Some(limit) = LIMIT {\n            if peak > limit {\n                alloc::handle_alloc_error(new_layout);\n            }\n        }\n\n        new_ptr\n    }\n}\n\npub(crate) struct AllocStat {\n    count: u64,\n    total: ByteSize,\n    peak: ByteSize,\n}\n\npub(crate) fn stat() -> AllocStat {\n    AllocStat {\n        count: ALLOC.count.load(Ordering::Relaxed),\n        total: ByteSize::b(ALLOC.total.load(Ordering::Relaxed)),\n        peak: ByteSize::b(ALLOC.peak.load(Ordering::Relaxed)),\n    }\n}\n\nimpl Display for AllocStat {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(\n            formatter,\n            \"{} allocations, total {}, peak {}\",\n            self.count, self.total, self.peak,\n        )\n    }\n}\n"
  },
  {
    "path": "src/arena.rs",
    "content": "use foldhash::HashMap;\nuse std::any::TypeId;\nuse std::fmt::{self, Debug};\nuse std::iter::Copied;\nuse std::ptr;\nuse std::slice::Iter;\nuse std::sync::OnceLock;\nuse std::sync::{Mutex, PoisonError};\nuse typed_arena::Arena;\n\n#[derive(Ord, PartialOrd, Eq, PartialEq, Hash)]\npub struct Slice<T: 'static> {\n    contents: &'static [T],\n}\n\nimpl<T> Slice<T>\nwhere\n    T: 'static,\n{\n    pub const EMPTY: Self = Slice { contents: &[] };\n\n    pub fn new(slice: &[T]) -> Self\n    where\n        T: Send + Clone,\n    {\n        slice.iter().cloned().collect()\n    }\n\n    pub const fn from(contents: &'static [T]) -> Self {\n        Slice { contents }\n    }\n\n    pub fn iter(&self) -> impl Iterator<Item = T>\n    where\n        T: Copy,\n    {\n        (*self).into_iter()\n    }\n\n    pub fn iter_ref(&self) -> impl Iterator<Item = &'static T> {\n        self.contents.iter()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.contents.is_empty()\n    }\n}\n\nimpl<T> Copy for Slice<T> where T: 'static {}\n\nimpl<T> Clone for Slice<T>\nwhere\n    T: 'static,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> FromIterator<T> for Slice<T>\nwhere\n    T: 'static + Send + Clone,\n{\n    fn from_iter<I>(iter: I) -> Self\n    where\n        I: IntoIterator<Item = T>,\n    {\n        let iter = iter.into_iter();\n        if iter.size_hint() == (0, Some(0)) {\n            return Slice::EMPTY;\n        }\n\n        static ARENA: OnceLock<Mutex<HashMap<TypeId, Box<dyn Send>>>> = OnceLock::new();\n\n        let mut map = ARENA\n            .get_or_init(Mutex::default)\n            .lock()\n            .unwrap_or_else(PoisonError::into_inner);\n        let arena: &Box<dyn Send> = map\n            .entry(TypeId::of::<T>())\n            .or_insert_with(|| Box::new(Arena::<T>::new()));\n        let arena = unsafe { &*(ptr::from_ref::<dyn Send>(&**arena).cast::<Arena<T>>()) };\n        Slice {\n            contents: arena.alloc_extend(iter),\n        }\n    }\n}\n\nimpl<T> IntoIterator for Slice<T>\nwhere\n    T: 'static + Copy,\n{\n    type Item = T;\n    type IntoIter = Copied<Iter<'static, T>>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.contents.iter().copied()\n    }\n}\n\nimpl<T> Debug for Slice<T>\nwhere\n    T: 'static + Debug,\n{\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Debug::fmt(self.contents, formatter)\n    }\n}\n"
  },
  {
    "path": "src/args.rs",
    "content": "use crate::{cratename, user};\nuse clap::builder::{ArgAction, ValueParser};\nuse clap::{Arg, Command};\nuse regex::Regex;\nuse semver::VersionReq;\nuse std::env;\nuse std::ffi::{OsStr, OsString};\nuse std::path::PathBuf;\nuse std::str::FromStr;\nuse thiserror::Error;\n\n#[derive(Debug)]\npub(crate) struct Opt {\n    pub db: PathBuf,\n    pub exclude: Vec<Regex>,\n    pub jobs: usize,\n    pub relative: bool,\n    pub title: Option<String>,\n    pub transitive: bool,\n    pub queries: Vec<String>,\n}\n\nconst USAGE: &str = \"\\\n    cargo tally [OPTIONS] QUERIES...\n    cargo tally serde:1.0 'anyhow:^1.0 + thiserror'\";\n\nconst TEMPLATE: &str = \"\\\n{bin} {version}\nDavid Tolnay <dtolnay@gmail.com>\nhttps://github.com/dtolnay/cargo-tally\n\n{usage-heading}\n    {usage}\n\n{all-args}\\\n\";\n\nfn app(jobs_help: &String) -> Command {\n    let mut app = Command::new(\"cargo-tally\")\n        .override_usage(USAGE)\n        .help_template(TEMPLATE)\n        .arg(arg_db())\n        .arg(arg_exclude())\n        .arg(arg_jobs(jobs_help))\n        .arg(arg_relative())\n        .arg(arg_title())\n        .arg(arg_transitive())\n        .arg(arg_queries());\n    if let Some(version) = option_env!(\"CARGO_PKG_VERSION\") {\n        app = app.version(version);\n    }\n    app\n}\n\nconst DB: &str = \"db\";\nconst EXCLUDE: &str = \"exclude\";\nconst JOBS: &str = \"jobs\";\nconst RELATIVE: &str = \"relative\";\nconst TITLE: &str = \"title\";\nconst TRANSITIVE: &str = \"transitive\";\nconst QUERIES: &str = \"queries\";\n\npub(crate) fn parse() -> Opt {\n    // | threads | duration | allocated |  peak   |\n    // |---------|----------|-----------|---------|\n    // |     1   |  38.6 s  |   55.2 GB | 11.0 GB |\n    // |     2   |  24.8 s  |   55.4 GB | 10.2 GB |\n    // |     4   |  14.2 s  |   55.8 GB |  8.8 GB |\n    // |     8   |  12.7 s  |   58.4 GB |  8.3 GB |\n    // |    16   |  12.6 s  |   59.2 GB |  8.2 GB |\n    // |    32   |  12.8 s  |   63.2 GB |  8.4 GB |\n    // |    64   |  14.0 s  |   69.5 GB | 11.1 GB |\n    let default_jobs = num_cpus::get().min(32);\n    let jobs_help = format!(\n        \"Number of threads to run differential dataflow [default: {}]\",\n        default_jobs,\n    );\n\n    let mut args: Vec<_> = env::args_os().collect();\n    if let Some(first) = args.get_mut(0) {\n        *first = OsString::from(\"cargo-tally\");\n    }\n    if args.get(1).map(OsString::as_os_str) == Some(OsStr::new(\"tally\")) {\n        args.remove(1);\n    }\n    let matches = app(&jobs_help).get_matches_from(args);\n\n    let db = PathBuf::from(matches.get_one::<PathBuf>(DB).unwrap());\n\n    let exclude = matches\n        .get_many::<Regex>(EXCLUDE)\n        .unwrap_or_default()\n        .cloned()\n        .collect();\n\n    let jobs = matches\n        .get_one::<usize>(JOBS)\n        .copied()\n        .unwrap_or(default_jobs);\n\n    let title = matches.get_one::<String>(TITLE).map(String::clone);\n\n    let relative = matches.get_flag(RELATIVE);\n    let transitive = matches.get_flag(TRANSITIVE);\n\n    let queries = matches\n        .get_many::<String>(QUERIES)\n        .unwrap()\n        .map(String::clone)\n        .collect();\n\n    Opt {\n        db,\n        exclude,\n        jobs,\n        relative,\n        title,\n        transitive,\n        queries,\n    }\n}\n\nfn arg_db() -> Arg {\n    Arg::new(DB)\n        .long(DB)\n        .num_args(1)\n        .value_name(\"PATH\")\n        .default_value(\"./db-dump.tar.gz\")\n        .value_parser(ValueParser::path_buf())\n        .help(\"Path to crates.io's database dump\")\n}\n\nfn arg_exclude() -> Arg {\n    Arg::new(EXCLUDE)\n        .long(EXCLUDE)\n        .hide(true)\n        .action(ArgAction::Append)\n        .value_name(\"REGEX\")\n        .value_parser(Regex::from_str)\n        .help(\"Ignore a dependency coming from any crates matching regex\")\n}\n\nfn arg_jobs(help: &String) -> Arg {\n    Arg::new(JOBS)\n        .long(JOBS)\n        .short('j')\n        .num_args(1)\n        .value_name(\"N\")\n        .value_parser(usize::from_str)\n        .help(help)\n}\n\nfn arg_relative() -> Arg {\n    Arg::new(RELATIVE)\n        .long(RELATIVE)\n        .num_args(0)\n        .help(\"Display as a fraction of total crates, not absolute number\")\n}\n\nfn arg_title() -> Arg {\n    Arg::new(TITLE)\n        .long(TITLE)\n        .hide(true)\n        .num_args(1)\n        .value_name(\"TITLE\")\n        .value_parser(ValueParser::string())\n        .help(\"Graph title\")\n}\n\nfn arg_transitive() -> Arg {\n    Arg::new(TRANSITIVE)\n        .long(TRANSITIVE)\n        .num_args(0)\n        .help(\"Count transitive dependencies, not just direct dependencies\")\n}\n\nfn arg_queries() -> Arg {\n    Arg::new(QUERIES)\n        .required(true)\n        .num_args(0..)\n        .value_name(\"QUERIES\")\n        .value_parser(validate_query)\n        .help(\"Queries\")\n        .hide(true)\n}\n\n#[derive(Error, Debug)]\nenum Error {\n    #[error(\"invalid crates.io username\")]\n    InvalidUsername,\n    #[error(\"invalid crate name according to crates.io\")]\n    InvalidCrateName,\n    #[error(transparent)]\n    Semver(#[from] semver::Error),\n}\n\nfn validate_query(string: &str) -> Result<String, Error> {\n    for predicate in string.split('+') {\n        let predicate = predicate.trim();\n\n        if let Some(username) = predicate.strip_prefix('@') {\n            if username.split('/').all(user::valid) {\n                continue;\n            } else {\n                return Err(Error::InvalidUsername);\n            }\n        }\n\n        let (name, req) = if let Some((name, req)) = predicate.split_once(':') {\n            (name, Some(req))\n        } else {\n            (predicate, None)\n        };\n\n        if !cratename::valid(name.trim()) {\n            return Err(Error::InvalidCrateName);\n        }\n\n        if let Some(req) = req {\n            VersionReq::from_str(req)?;\n        }\n    }\n\n    Ok(string.to_owned())\n}\n\n#[test]\nfn test_cli() {\n    let jobs_help = String::new();\n    app(&jobs_help).debug_assert();\n}\n"
  },
  {
    "path": "src/clean.rs",
    "content": "use crate::cratemap::CrateMap;\nuse cargo_tally::arena::Slice;\nuse cargo_tally::id::{CrateId, VersionId};\nuse cargo_tally::version::Version;\nuse cargo_tally::{DbDump, Dependency};\nuse semver::{Comparator, Op};\nuse std::cmp;\nuse std::collections::btree_map::{BTreeMap as Map, Entry};\n\npub(crate) fn clean(db_dump: &mut DbDump, crates: &CrateMap) {\n    let mut crate_max_version: Map<CrateId, &Version> = Map::new();\n    let mut dependencies_per_version: Map<VersionId, Vec<&mut Dependency>> = Map::new();\n\n    for dep in &mut db_dump.dependencies {\n        dependencies_per_version\n            .entry(dep.version_id)\n            .or_insert_with(Vec::new)\n            .push(dep);\n    }\n\n    for rel in &db_dump.releases {\n        match crate_max_version.entry(rel.crate_id) {\n            Entry::Vacant(entry) => {\n                entry.insert(&rel.num);\n            }\n            Entry::Occupied(entry) => {\n                let entry = entry.into_mut();\n                *entry = cmp::max(entry, &rel.num);\n            }\n        }\n\n        let mut no_dependencies = Vec::new();\n        let dependencies = dependencies_per_version\n            .get_mut(&rel.id)\n            .unwrap_or(&mut no_dependencies);\n        let mut i = 0;\n        while let Some(dep) = dependencies.get_mut(i) {\n            if !crate_max_version.contains_key(&dep.crate_id) {\n                // If every published version of a crate is a prerelease, Cargo\n                // will resolve a `*` wildcard dependency to the max prerelease,\n                // which we don't track.\n                //\n                // Other times, crates just go missing from the index, maybe for\n                // legal reasons or because of leaked secrets.\n                // https://github.com/rust-lang/crates.io-index/commit/a95f8bff541de7461638b5e4f75ee58747829ea3\n                if crate::trace::VERBOSE {\n                    eprintln!(\n                        \"unresolved dep {} {} on {} {}\",\n                        crates.name(rel.crate_id).unwrap(),\n                        rel.num,\n                        crates.name(dep.crate_id).unwrap(),\n                        dep.req,\n                    );\n                }\n                dependencies.remove(i);\n                continue;\n            }\n            let max_version = crate_max_version[&dep.crate_id];\n            let mut incompatible_version = Version(semver::Version {\n                major: 0,\n                minor: 0,\n                patch: 0,\n                pre: semver::Prerelease::EMPTY,\n                build: semver::BuildMetadata::EMPTY,\n            });\n            // Produce a synthetic version which is semver incompatible with the\n            // highest version currently published.\n            if max_version.major > 0 {\n                incompatible_version.major = max_version.major + 1;\n            } else if max_version.minor > 0 {\n                incompatible_version.minor = max_version.minor + 1;\n            } else {\n                incompatible_version.patch = max_version.patch + 1;\n            }\n            if dep.req.matches(&incompatible_version) {\n                // If the declared dependency requirement claims this crate\n                // works with the incompatible future release, we deem the\n                // dependency silly and constrain it to remain compatible with\n                // the current max published. This affects reqs like `0.*`.\n                dep.req.comparators = Slice::new(&[Comparator {\n                    op: Op::Caret,\n                    major: max_version.major,\n                    minor: Some(max_version.minor),\n                    patch: Some(max_version.patch),\n                    pre: semver::Prerelease::EMPTY,\n                }]);\n            }\n            i += 1;\n        }\n    }\n}\n"
  },
  {
    "path": "src/collect.rs",
    "content": "use differential_dataflow::collection::Collection;\nuse differential_dataflow::difference::Semigroup;\nuse std::mem;\nuse std::sync::{Arc, Mutex, PoisonError};\nuse timely::dataflow::Scope;\nuse timely::Data;\n\npub(crate) trait Collect<T> {\n    fn collect_into(&self, result: &Emitter<T>);\n}\n\npub(crate) struct ResultCollection<T> {\n    out: Arc<Mutex<Vec<T>>>,\n}\n\npub(crate) struct Emitter<T> {\n    out: Arc<Mutex<Vec<T>>>,\n}\n\nimpl<T> ResultCollection<T> {\n    pub(crate) fn new() -> Self {\n        let out = Arc::new(Mutex::new(Vec::new()));\n        ResultCollection { out }\n    }\n\n    pub(crate) fn emitter(&self) -> Emitter<T> {\n        let out = Arc::clone(&self.out);\n        Emitter { out }\n    }\n}\n\nimpl<D, T, R> ResultCollection<(D, T, R)>\nwhere\n    T: Ord,\n{\n    pub(crate) fn sort(&self) {\n        self.out\n            .lock()\n            .unwrap_or_else(PoisonError::into_inner)\n            .sort_by(\n                |(_ldata, ltimestamp, _ldiff), (_rdata, rtimestamp, _rdiff)| {\n                    ltimestamp.cmp(rtimestamp)\n                },\n            );\n    }\n}\n\nimpl<G, D, R> Collect<(D, G::Timestamp, R)> for Collection<G, D, R>\nwhere\n    G: Scope,\n    D: Data,\n    R: Semigroup,\n    G::Timestamp: Data,\n{\n    fn collect_into(&self, result: &Emitter<(D, G::Timestamp, R)>) {\n        let out = Arc::clone(&result.out);\n        self.inspect_batch(move |_timestamp, slice| {\n            out.lock()\n                .unwrap_or_else(PoisonError::into_inner)\n                .extend_from_slice(slice);\n        });\n    }\n}\n\nimpl<T> IntoIterator for ResultCollection<T> {\n    type Item = T;\n    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        let mut out = self.out.lock().unwrap_or_else(PoisonError::into_inner);\n        mem::take(&mut *out).into_iter()\n    }\n}\n"
  },
  {
    "path": "src/communication.rs",
    "content": "// As far as I can tell, timely dataflow uses abomonation only for interprocess\n// communication. Within a single process, it uses the Clone impl instead. We\n// stub out the Abomonation impl since it will never be called.\nmacro_rules! do_not_abomonate {\n    ($($path:ident)::+ $(<$param:ident>)? $(where $($clause:tt)*)?) => {\n        impl $(<$param>)? abomonation::Abomonation for $($path)::+ $(<$param>)? $(where $($clause)*)? {\n            unsafe fn entomb<W: std::io::Write>(&self, _write: &mut W) -> std::io::Result<()> {\n                unimplemented!(\"unexpected abomonation entomb\");\n            }\n            unsafe fn exhume<'a>(&mut self, _bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {\n                // Unwinding here is unsound because abomonation would have\n                // blitted the source bytes into the destination with dangling\n                // pointers, and is now relying on exhume to fix it up into a\n                // valid object. We abort instead.\n                std::process::exit(1);\n            }\n            fn extent(&self) -> usize {\n                unimplemented!(\"unexpected abomonation extent\");\n            }\n        }\n\n        impl $(<$param>)? serde::Serialize for $($path)::+ $(<$param>)? $(where $($clause)*)? {\n            fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>\n            where\n                S: serde::Serializer,\n            {\n                unimplemented!(\"unexpected serde serialize\");\n            }\n        }\n\n        impl<'de, $($param)?> serde::Deserialize<'de> for $($path)::+ $(<$param>)? $(where $($clause)*)? {\n            fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>\n            where\n                D: serde::Deserializer<'de>,\n            {\n                unimplemented!(\"unexpected serde deserialize\");\n            }\n        }\n    };\n}\n\ndo_not_abomonate!(crate::Dependency);\ndo_not_abomonate!(crate::Query);\ndo_not_abomonate!(crate::Release);\ndo_not_abomonate!(crate::arena::Slice<T> where T: 'static);\ndo_not_abomonate!(crate::feature::CrateFeature);\ndo_not_abomonate!(crate::feature::DefaultFeatures);\ndo_not_abomonate!(crate::feature::FeatureId);\ndo_not_abomonate!(crate::feature::VersionFeature);\ndo_not_abomonate!(crate::id::CrateId);\ndo_not_abomonate!(crate::id::DependencyId);\ndo_not_abomonate!(crate::id::QueryId);\ndo_not_abomonate!(crate::id::VersionId);\ndo_not_abomonate!(crate::max::Max<T>);\ndo_not_abomonate!(crate::present::Present);\ndo_not_abomonate!(crate::timestamp::DateTime);\ndo_not_abomonate!(crate::version::Version);\ndo_not_abomonate!(crate::version::VersionReq);\n"
  },
  {
    "path": "src/cratemap.rs",
    "content": "use crate::cratename::{CrateName, CrateNameQuery};\nuse crate::user::User;\nuse cargo_tally::id::CrateId;\nuse db_dump::crate_owners::OwnerId;\nuse ref_cast::RefCast;\nuse std::collections::BTreeMap as Map;\n\n#[derive(Default)]\npub struct CrateMap {\n    names: Map<CrateId, String>,\n    ids: Map<CrateName, CrateId>,\n    pub(crate) users: Map<User, OwnerId>,\n    pub(crate) owners: Map<OwnerId, Vec<CrateId>>,\n}\n\nimpl CrateMap {\n    pub fn new() -> Self {\n        CrateMap::default()\n    }\n\n    pub fn insert(&mut self, id: CrateId, name: String) {\n        assert!(!self.ids.contains_key(CrateNameQuery::ref_cast(&name)));\n        assert!(!self.names.contains_key(&id));\n        self.ids.insert(CrateName::new(name.clone()), id);\n        self.names.insert(id, name);\n    }\n\n    pub fn name(&self, id: CrateId) -> Option<&str> {\n        self.names.get(&id).map(String::as_str)\n    }\n\n    pub fn id(&self, name: &str) -> Option<CrateId> {\n        self.ids.get(CrateNameQuery::ref_cast(name)).copied()\n    }\n}\n"
  },
  {
    "path": "src/cratename.rs",
    "content": "use ref_cast::RefCast;\nuse std::borrow::Borrow;\nuse std::cmp::Ordering;\n\npub const MAX_NAME_LENGTH: usize = 64;\n\n// Mirrored from https://github.com/rust-lang/crates.io/blob/54a3f10794db7f57e3602426389c369290a8a3d5/src/models/krate.rs\npub fn valid(name: &str) -> bool {\n    let under_max_length = name.chars().take(MAX_NAME_LENGTH + 1).count() <= MAX_NAME_LENGTH;\n    valid_ident(name) && under_max_length\n}\n\nfn valid_ident(name: &str) -> bool {\n    valid_feature_prefix(name) && name.chars().next().is_some_and(char::is_alphabetic)\n}\n\nfn valid_feature_prefix(name: &str) -> bool {\n    !name.is_empty()\n        && name\n            .chars()\n            .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')\n}\n\npub(crate) struct CrateName(String);\n\nimpl CrateName {\n    pub(crate) fn new(string: String) -> Self {\n        CrateName(string)\n    }\n}\n\nimpl Ord for CrateName {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        CrateNameQuery::ref_cast(&self.0).cmp(CrateNameQuery::ref_cast(&rhs.0))\n    }\n}\n\nimpl PartialOrd for CrateName {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for CrateName {}\n\nimpl PartialEq for CrateName {\n    fn eq(&self, rhs: &Self) -> bool {\n        CrateNameQuery::ref_cast(&self.0).eq(CrateNameQuery::ref_cast(&rhs.0))\n    }\n}\n\n#[derive(RefCast)]\n#[repr(transparent)]\npub(crate) struct CrateNameQuery(str);\n\nimpl Borrow<CrateNameQuery> for CrateName {\n    fn borrow(&self) -> &CrateNameQuery {\n        CrateNameQuery::ref_cast(&self.0)\n    }\n}\n\nimpl Ord for CrateNameQuery {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        self.0\n            .bytes()\n            .map(SeparatorAgnosticByte)\n            .cmp(rhs.0.bytes().map(SeparatorAgnosticByte))\n    }\n}\n\nimpl PartialOrd for CrateNameQuery {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for CrateNameQuery {}\n\nimpl PartialEq for CrateNameQuery {\n    fn eq(&self, rhs: &Self) -> bool {\n        self.0\n            .bytes()\n            .map(SeparatorAgnosticByte)\n            .eq(rhs.0.bytes().map(SeparatorAgnosticByte))\n    }\n}\n\nstruct SeparatorAgnosticByte(u8);\n\nimpl Ord for SeparatorAgnosticByte {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        let lhs = if self.0 == b'_' { b'-' } else { self.0 };\n        let rhs = if rhs.0 == b'_' { b'-' } else { rhs.0 };\n        lhs.cmp(&rhs)\n    }\n}\n\nimpl PartialOrd for SeparatorAgnosticByte {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for SeparatorAgnosticByte {}\n\nimpl PartialEq for SeparatorAgnosticByte {\n    fn eq(&self, rhs: &Self) -> bool {\n        self.cmp(rhs) == Ordering::Equal\n    }\n}\n"
  },
  {
    "path": "src/dependency.rs",
    "content": "#[derive(Copy, Clone, Debug)]\npub enum DependencyKind {\n    Normal,\n    Build,\n    Dev,\n}\n\nimpl From<db_dump::dependencies::DependencyKind> for DependencyKind {\n    fn from(dependency_kind: db_dump::dependencies::DependencyKind) -> Self {\n        match dependency_kind {\n            db_dump::dependencies::DependencyKind::Normal => DependencyKind::Normal,\n            db_dump::dependencies::DependencyKind::Build => DependencyKind::Build,\n            db_dump::dependencies::DependencyKind::Dev => DependencyKind::Dev,\n        }\n    }\n}\n"
  },
  {
    "path": "src/feature.rs",
    "content": "use crate::arena::Slice;\nuse crate::id::{CrateId, VersionId};\nuse std::collections::BTreeMap as Map;\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\n#[repr(transparent)]\npub struct FeatureId(pub u32);\n\nimpl FeatureId {\n    pub const CRATE: Self = FeatureId(0);\n    pub const DEFAULT: Self = FeatureId(1);\n    pub const TBD: Self = FeatureId(!0);\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct FeatureEnables {\n    pub id: FeatureId,\n    pub enables: Slice<CrateFeature>,\n    pub weak_enables: Slice<CrateFeature>,\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct CrateFeature {\n    pub crate_id: CrateId,\n    pub feature_id: FeatureId,\n}\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\npub struct VersionFeature {\n    pub version_id: VersionId,\n    pub feature_id: FeatureId,\n}\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]\npub struct DefaultFeatures(pub bool);\n\npub struct FeatureNames {\n    names: Vec<String>,\n    map: Map<String, FeatureId>,\n}\n\nimpl FeatureNames {\n    pub fn new() -> Self {\n        let mut feature_names = FeatureNames {\n            names: Vec::new(),\n            map: Map::new(),\n        };\n        assert_eq!(feature_names.id(\"\"), FeatureId::CRATE);\n        assert_eq!(feature_names.id(\"default\"), FeatureId::DEFAULT);\n        feature_names\n    }\n\n    pub fn id(&mut self, name: &str) -> FeatureId {\n        if let Some(id) = self.map.get(name) {\n            *id\n        } else {\n            let new_id = FeatureId(u32::try_from(self.names.len()).unwrap());\n            self.names.push(name.to_owned());\n            self.map.insert(name.to_owned(), new_id);\n            new_id\n        }\n    }\n\n    pub fn name(&self, id: FeatureId) -> &str {\n        &self.names[id.0 as usize]\n    }\n}\n\nimpl Default for FeatureNames {\n    fn default() -> Self {\n        FeatureNames::new()\n    }\n}\n\npub struct FeatureIter {\n    krate: bool,\n    default: bool,\n    other: <Slice<FeatureId> as IntoIterator>::IntoIter,\n}\n\nimpl FeatureIter {\n    pub fn new(default_features: DefaultFeatures, features: Slice<FeatureId>) -> Self {\n        FeatureIter {\n            krate: !default_features.0 && features.is_empty(),\n            default: default_features.0,\n            other: features.into_iter(),\n        }\n    }\n}\n\nimpl Iterator for FeatureIter {\n    type Item = FeatureId;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.krate {\n            self.krate = false;\n            Some(FeatureId::CRATE)\n        } else if self.default {\n            self.default = false;\n            Some(FeatureId::DEFAULT)\n        } else {\n            self.other.next()\n        }\n    }\n}\n"
  },
  {
    "path": "src/filter.rs",
    "content": "use crate::cratemap::CrateMap;\nuse cargo_tally::DbDump;\nuse regex::Regex;\n\npub(crate) fn filter(db_dump: &mut DbDump, crates: &CrateMap, exclude: &[Regex]) {\n    if exclude.is_empty() {\n        return;\n    }\n    db_dump.releases.retain(|rel| {\n        let crate_name = crates.name(rel.crate_id).unwrap();\n        exclude.iter().all(|exclude| !exclude.is_match(crate_name))\n    });\n}\n"
  },
  {
    "path": "src/hidden.rs",
    "content": "// There is no library public API. Only the command line tool is considered\n// public API.\n\n#[path = \"lib.rs\"]\nmod lib;\n\n#[doc(hidden)]\npub use crate::lib::*;\n"
  },
  {
    "path": "src/hint.rs",
    "content": "use differential_dataflow::collection::Collection;\nuse differential_dataflow::difference::Semigroup;\nuse timely::dataflow::Scope;\n\n#[allow(non_snake_case)]\npub(crate) trait TypeHint: Sized {\n    type Element;\n\n    fn T<D>(self) -> Self\n    where\n        Self: TypeHint<Element = D>,\n    {\n        self\n    }\n\n    fn KV<K, V>(self) -> Self\n    where\n        Self: TypeHint<Element = (K, V)>,\n    {\n        self\n    }\n}\n\nimpl<G, D, R> TypeHint for Collection<G, D, R>\nwhere\n    G: Scope,\n    R: Semigroup,\n{\n    type Element = D;\n}\n\nimpl<G, D, R> TypeHint for &Collection<G, D, R>\nwhere\n    G: Scope,\n    R: Semigroup,\n{\n    type Element = D;\n}\n"
  },
  {
    "path": "src/id.rs",
    "content": "#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\n#[repr(transparent)]\npub struct QueryId(pub u8);\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\n#[repr(transparent)]\npub struct CrateId(pub u32);\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\n#[repr(transparent)]\npub struct VersionId(pub u32);\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]\n#[repr(transparent)]\npub struct DependencyId(pub u32);\n\nimpl From<db_dump::crates::CrateId> for CrateId {\n    fn from(id: db_dump::crates::CrateId) -> Self {\n        CrateId(id.0)\n    }\n}\n\nimpl From<db_dump::versions::VersionId> for VersionId {\n    fn from(id: db_dump::versions::VersionId) -> Self {\n        VersionId(id.0)\n    }\n}\n\nimpl From<u32> for DependencyId {\n    fn from(id: u32) -> Self {\n        DependencyId(id)\n    }\n}\n"
  },
  {
    "path": "src/impls.rs",
    "content": "use crate::{Dependency, Query, Release};\nuse std::cmp::Ordering;\n\nimpl Ord for Query {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.id.cmp(&other.id)\n    }\n}\n\nimpl PartialOrd for Query {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Eq for Query {}\n\nimpl PartialEq for Query {\n    fn eq(&self, other: &Self) -> bool {\n        self.id == other.id\n    }\n}\n\nimpl Ord for Release {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.id.cmp(&other.id)\n    }\n}\n\nimpl PartialOrd for Release {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Eq for Release {}\n\nimpl PartialEq for Release {\n    fn eq(&self, other: &Self) -> bool {\n        self.id == other.id\n    }\n}\n\nimpl Ord for Dependency {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.id.cmp(&other.id)\n    }\n}\n\nimpl PartialOrd for Dependency {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Eq for Dependency {}\n\nimpl PartialEq for Dependency {\n    fn eq(&self, other: &Self) -> bool {\n        self.id == other.id\n    }\n}\n"
  },
  {
    "path": "src/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\" />\n  <script src=\"https://d3js.org/d3.v7.min.js\"></script>\n  <style>\n    body {\n      font: 14px sans-serif;\n    }\n    .axis path, .axis line {\n      shape-rendering: crispEdges;\n    }\n    .line {\n      fill: none;\n      stroke-width: 1.5px;\n    }\n    svg {\n      overflow: visible;\n    }\n  </style>\n</head>\n<body>\n  <script>\n    var data = CARGO_TALLY_DATA;\n\n    var margin = { top: 20, right: 100, bottom: 30, left: 50 };\n    var width = 950 - margin.left - margin.right;\n    var height = 500 - margin.top - margin.bottom;\n\n    var x = d3.scaleTime().range([0, width]);\n    var y = d3.scaleLinear().range([height, 0]);\n    var color = d3.scaleOrdinal(d3.schemeCategory10);\n    var xAxis = d3.axisBottom(x);\n    var yAxis = d3.axisLeft(y);\n\n    var line = d3.line()\n      .x(function(d) {\n        return x(d.time);\n      })\n      .y(function(d) {\n        return y(d.edges);\n      });\n\n    color.domain(data.map(function(dataset) {\n      return dataset.name;\n    }));\n\n    data.forEach(function(dataset) {\n      dataset.values.forEach(function(d) {\n        d.time = new Date(d.time);\n      });\n    });\n    \n    var minDate = d3.min(data, function(dataset) {\n      return dataset.values[0].time;\n    });\n    var maxDate = d3.max(data, function(dataset) {\n      return dataset.values[dataset.values.length - 1].time;\n    });\n    var maxValue = d3.max(data, function(c) {\n      return d3.max(c.values, function(v) {\n        return v.edges;\n      });\n    });\n    x.domain([(21 * minDate - maxDate) / 20, maxDate]);\n    y.domain([0, 1.025 * maxValue]);\n\n#if CARGO_TALLY_RELATIVE\n    // NOTE The ticks should be read only after setting the domain values on `y`\n    var stepSize = y.ticks()[1] - y.ticks()[0]\n    var yFormatter = d3.format(`.${Math.max(0, d3.precisionFixed(stepSize) - 2)}%`);\n    var tooltipFormatter = d3.format(`.${Math.max(1, d3.precisionFixed(stepSize / 10) - 2)}%`);\n#else\n    var yFormatter = d3.format(\",\");\n    var tooltipFormatter = d3.format(\",\");\n#endif\n    yAxis.tickFormat(yFormatter);\n\n    var svg = d3.select(\"body\")\n      .append(\"svg\")\n      .attr(\"width\", width + margin.left + margin.right)\n      .attr(\"height\", height + margin.top + margin.bottom)\n      .append(\"g\")\n      .attr(\"transform\", `translate(${margin.left} ${margin.top})`);\n\n    var filter = svg.append(\"defs\")\n      .append(\"filter\")\n      .attr(\"x\", \"0\")\n      .attr(\"y\", \"0\")\n      .attr(\"width\", \"1\")\n      .attr(\"height\", \"1\")\n      .attr(\"id\", \"solid\");\n    filter.append(\"feFlood\")\n      .attr(\"flood-color\", \"white\");\n    filter.append(\"feComposite\")\n      .attr(\"in\", \"SourceGraphic\");\n\n    var legend = svg.selectAll()\n      .data(data)\n      .enter()\n      .append(\"g\");\n\n    legend.append(\"rect\")\n      .attr(\"x\", 50)\n      .attr(\"y\", function(d, i) {\n        return i * 20;\n      })\n      .attr(\"width\", 10)\n      .attr(\"height\", 10)\n      .style(\"fill\", function(d) {\n        return color(d.name);\n      });\n\n    legend.append(\"text\")\n      .attr(\"x\", 64)\n      .attr(\"y\", function(d, i) {\n        return (i * 20) + 9;\n      })\n      .text(function(d) {\n        return d.name;\n      });\n\n    svg.append(\"g\")\n      .attr(\"class\", \"x axis\")\n      .attr(\"transform\", `translate(0 ${height})`)\n      .call(xAxis);\n\n    svg.append(\"g\")\n      .attr(\"class\", \"y axis\")\n      .call(yAxis);\n\n    svg.append(\"text\")\n      .attr(\"transform\", \"rotate(-90)\")\n      .attr(\"y\", 6)\n      .attr(\"dy\", \".71em\")\n      .style(\"text-anchor\", \"end\")\n      .text(CARGO_TALLY_TITLE);\n\n    var curve = svg.selectAll()\n      .data(data)\n      .enter()\n      .append(\"g\");\n\n    curve.append(\"path\")\n      .attr(\"class\", \"line\")\n      .attr(\"d\", function(d) {\n        return line(d.values);\n      })\n      .style(\"stroke\", function(d) {\n        return color(d.name);\n      })\n      .style(\"stroke-linejoin\", \"round\");\n\n    curve.append(\"text\")\n      .attr(\"transform\", function(d) {\n        var last = d.values[d.values.length - 1];\n        return `translate(${x(last.time)} ${y(last.edges)})`;\n      })\n      .attr(\"x\", 3)\n      .attr(\"dy\", \".35em\")\n      .text(function(d) {\n        return d.name;\n      });\n\n    var mouseG = svg.append(\"g\")\n      .style(\"opacity\", \"0\");\n\n    mouseG.append(\"path\") // this is the black vertical line to follow mouse\n      .style(\"stroke\", \"black\")\n      .style(\"stroke-width\", \"1px\")\n      .attr(\"d\", `M0 ${height + xAxis.tickSize()} 0 0`);\n\n    var mouseDate = mouseG.append(\"text\")\n      .attr(\"y\", height + 9)\n      .attr(\"dy\", \"0.71em\")\n      .attr(\"text-anchor\", \"middle\")\n      .attr(\"filter\", \"url(#solid)\");\n\n    var mousePerLine = mouseG.selectAll()\n      .data(data)\n      .enter()\n      .append(\"g\")\n      .attr(\"class\", \"mouse-per-line\")\n      .style(\"opacity\", \"0\");\n\n    mousePerLine.append(\"circle\")\n      .attr(\"r\", 7)\n      .style(\"stroke\", function(d) {\n        return color(d.name);\n      })\n      .style(\"fill\", \"none\")\n      .style(\"stroke-width\", \"1px\");\n\n    mousePerLine.append(\"text\")\n      .attr(\"x\", -6)\n      .attr(\"y\", -4)\n      .style(\"text-anchor\", \"end\");\n\n    svg.append(\"rect\") // append a rect to catch mouse movements on canvas\n      .attr(\"width\", width) // can't catch mouse events on a g element\n      .attr(\"height\", height + xAxis.tickSize() + 16)\n      .attr(\"fill\", \"none\")\n      .attr(\"pointer-events\", \"all\")\n      .on(\"mouseout\", function() { // on mouse out hide line, circles and text\n        mouseG.style(\"opacity\", \"0\");\n      })\n      .on(\"mouseover\", function() { // on mouse in show line, circles and text\n        mouseG.style(\"opacity\", \"1\");\n      })\n      .on(\"mousemove\", function(event) { // mouse moving over canvas\n        var mouse = d3.pointer(event);\n        mouseG.attr(\"transform\", `translate(${mouse[0]} 0)`);\n        mousePerLine.attr(\"transform\", function(d, i) {\n          var xDate = x.invert(mouse[0]);\n          var bisect = d3.bisector(function(d) { return d.time; }).right;\n          var idx = bisect(d.values, xDate);\n          var below = d.values[idx - (idx > 0)];\n          var above = d.values[idx - (idx == d.values.length)];\n          var interp = below.time == above.time ? 0 : (xDate - below.time) / (above.time - below.time);\n          var val = d3.interpolateNumber(below.edges, above.edges)(interp);\n\n          d3.select(this)\n            .style(\"opacity\", below.edges ? \"1\" : \"0\")\n            .select(\"text\")\n            .text(tooltipFormatter(below.edges));\n\n          mouseDate.text(d3.timeFormat(\"%b %-d\")(xDate));\n\n          return `translate(0 ${y(val)})`;\n        });\n      });\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![deny(unsafe_op_in_unsafe_fn)]\n#![allow(non_camel_case_types)]\n#![allow(\n    clippy::arc_with_non_send_sync, // https://github.com/rust-lang/rust-clippy/issues/11076\n    clippy::borrow_as_ptr,\n    clippy::borrowed_box,\n    clippy::cast_possible_truncation,\n    clippy::cast_precision_loss,\n    clippy::cast_ptr_alignment,\n    clippy::cast_sign_loss,\n    clippy::elidable_lifetime_names,\n    clippy::into_iter_without_iter,\n    clippy::items_after_statements,\n    clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285\n    clippy::let_underscore_untyped,\n    clippy::mismatching_type_param_order, // https://github.com/rust-lang/rust-clippy/issues/8962\n    clippy::missing_errors_doc,\n    clippy::missing_panics_doc,\n    clippy::module_name_repetitions,\n    clippy::must_use_candidate,\n    clippy::needless_lifetimes,\n    clippy::needless_pass_by_value,\n    clippy::significant_drop_in_scrutinee,\n    clippy::too_many_lines,\n    clippy::uninlined_format_args,\n    clippy::unseparated_literal_suffix\n)]\n#![allow(unknown_lints, mismatched_lifetime_syntaxes)]\n\n#[macro_use]\nmod stream;\n\npub mod arena;\npub(crate) mod collect;\nmod communication;\npub mod dependency;\npub mod feature;\npub(crate) mod hint;\npub mod id;\nmod impls;\npub mod matrix;\npub(crate) mod max;\npub(crate) mod present;\npub mod timestamp;\npub mod version;\n\nuse crate::arena::Slice;\nuse crate::collect::{Collect, Emitter, ResultCollection};\nuse crate::dependency::DependencyKind;\nuse crate::feature::{\n    DefaultFeatures, FeatureEnables, FeatureId, FeatureIter, FeatureNames, VersionFeature,\n};\nuse crate::hint::TypeHint;\nuse crate::id::{CrateId, DependencyId, QueryId, VersionId};\nuse crate::matrix::Matrix;\nuse crate::max::MaxByKey;\nuse crate::present::Present;\nuse crate::timestamp::{DateTime, Duration};\nuse crate::version::{Version, VersionReq};\nuse atomic_take::AtomicTake;\nuse differential_dataflow::input::InputSession;\nuse differential_dataflow::operators::arrange::{ArrangeByKey, ArrangeBySelf};\nuse differential_dataflow::operators::iterate::Variable;\nuse differential_dataflow::operators::{Join, JoinCore, Threshold};\nuse std::env;\nuse std::iter::once;\nuse std::net::TcpStream;\nuse std::ops::Deref;\nuse timely::communication::allocator::Process;\nuse timely::dataflow::operators::capture::EventWriter;\nuse timely::dataflow::scopes::Child;\nuse timely::dataflow::Scope;\nuse timely::logging::{BatchLogger, TimelyEvent};\nuse timely::order::Product;\nuse timely::progress::Timestamp;\nuse timely::worker::{Config as WorkerConfig, Worker};\n\n#[derive(Default)]\npub struct DbDump {\n    pub releases: Vec<Release>,\n    pub dependencies: Vec<Dependency>,\n    pub features: FeatureNames,\n}\n\n#[derive(Clone, Debug)]\npub struct Release {\n    pub id: VersionId,\n    pub crate_id: CrateId,\n    pub num: Version,\n    pub created_at: DateTime,\n    pub features: Slice<FeatureEnables>,\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct Dependency {\n    pub id: DependencyId,\n    pub version_id: VersionId,\n    pub crate_id: CrateId,\n    pub req: VersionReq,\n    pub feature_id: FeatureId,\n    pub default_features: DefaultFeatures,\n    pub features: Slice<FeatureId>,\n    pub kind: DependencyKind,\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct Query {\n    pub id: QueryId,\n    pub predicates: Slice<Predicate>,\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct Predicate {\n    pub crate_id: CrateId,\n    pub req: Option<VersionReq>,\n}\n\n#[derive(Default)]\nstruct Input {\n    db_dump: DbDump,\n    queries: Vec<Query>,\n}\n\npub fn run(db_dump: DbDump, jobs: usize, transitive: bool, queries: &[Query]) -> Matrix {\n    let num_queries = queries.len();\n    let queries = queries.to_owned();\n    let input = AtomicTake::new(Input { db_dump, queries });\n    let collection = ResultCollection::<(QueryId, DateTime, isize)>::new();\n    let results = collection.emitter();\n\n    let allocators = Process::new_vector(jobs);\n    let other = Box::new(());\n    timely::communication::initialize_from(allocators, other, move |allocator| {\n        let mut worker = Worker::new(WorkerConfig::default(), allocator);\n        set_timely_worker_log(&worker);\n\n        let mut queries = InputSession::<DateTime, Query, Present>::new();\n        let mut releases = InputSession::<DateTime, Release, Present>::new();\n        let mut dependencies = InputSession::<DateTime, Dependency, Present>::new();\n\n        worker.dataflow(|scope| {\n            dataflow(\n                scope,\n                &mut queries,\n                &mut releases,\n                &mut dependencies,\n                transitive,\n                &results,\n            );\n        });\n\n        let input = input.take().unwrap_or_default();\n\n        for query in input.queries {\n            queries.update(query, Present);\n        }\n        queries.close();\n\n        for dep in input.db_dump.dependencies {\n            dependencies.update(dep, Present);\n        }\n        dependencies.close();\n\n        for rel in input.db_dump.releases {\n            releases.advance_to(rel.created_at);\n            releases.update(rel, Present);\n        }\n        releases.close();\n\n        while worker.step_or_park(None) {}\n    })\n    .unwrap();\n\n    let mut time = DateTime::minimum();\n    let mut values = vec![0u32; num_queries];\n    let mut matrix = Matrix::new(num_queries);\n    collection.sort();\n    for (i, (query_id, timestamp, diff)) in collection.into_iter().enumerate() {\n        if timestamp > time {\n            if i > 0 {\n                matrix.push(time, values.clone());\n            }\n            time = timestamp;\n        }\n        let cell = &mut values[query_id.0 as usize];\n        if diff > 0 {\n            *cell += diff as u32;\n        } else {\n            *cell = cell.checked_sub(-diff as u32).expect(\"value went negative\");\n        }\n    }\n    if match matrix.iter().next_back() {\n        Some((_timestamp, last)) => values != **last,\n        None => values.iter().any(|&n| n != 0),\n    } {\n        matrix.push(time, values);\n    }\n    matrix\n}\n\nfn set_timely_worker_log(worker: &Worker<Process>) {\n    let Some(addr) = env::var_os(\"TIMELY_WORKER_LOG_ADDR\") else {\n        return;\n    };\n\n    let stream = match TcpStream::connect(addr.to_str().unwrap()) {\n        Ok(stream) => stream,\n        Err(err) => panic!(\n            \"Could not connect logging stream to {addr}: {err}\",\n            addr = addr.display(),\n        ),\n    };\n\n    worker.log_register().insert::<TimelyEvent, _>(\"timely\", {\n        let writer = EventWriter::new(stream);\n        let mut logger = BatchLogger::new(writer);\n        move |time, data| logger.publish_batch(time, data)\n    });\n}\n\nfn dataflow(\n    scope: &mut Child<Worker<Process>, DateTime>,\n    queries: &mut InputSession<DateTime, Query, Present>,\n    releases: &mut InputSession<DateTime, Release, Present>,\n    dependencies: &mut InputSession<DateTime, Dependency, Present>,\n    transitive: bool,\n    results: &Emitter<(QueryId, DateTime, isize)>,\n) {\n    type queries<'a> = stream![Query; Present];\n    let queries: queries = queries.to_collection(scope);\n\n    type releases<'a> = stream![Release; Present];\n    let releases: releases = releases.to_collection(scope);\n\n    type dependencies<'a> = stream![Dependency; Present];\n    let dependencies: dependencies = dependencies.to_collection(scope);\n\n    // the version ids and version numbers that exist of each crate\n    type releases_by_crate_id<'a> = stream![CrateId => (VersionId, Version); Present];\n    let releases_by_crate_id: releases_by_crate_id =\n        releases.map(|rel| (rel.crate_id, (rel.id, rel.num)));\n    let releases_by_crate_id = releases_by_crate_id.arrange_by_key();\n\n    // for each dependency spec, what release does it refer to currently?\n    type resolved<'a> = stream![(CrateId, VersionReq) => VersionId; isize];\n    let resolved: resolved = dependencies\n        .map(|dep| (dep.crate_id, dep.req))\n        .KV::<CrateId, VersionReq>()\n        .join_core(\n            &releases_by_crate_id,\n            |crate_id, req, (version_id, version)| {\n                req.matches(version)\n                    .then(|| ((*crate_id, *req), (version.clone(), *version_id)))\n            },\n        )\n        .KV::<(CrateId, VersionReq), (Version, VersionId)>()\n        .max_by_key()\n        .KV::<(CrateId, VersionReq), (Version, VersionId)>()\n        .map(|((crate_id, req), (_version, version_id))| ((crate_id, req), version_id));\n    let resolved = resolved.arrange_by_key();\n\n    // full dependency graph across all versions of all crates\n    type dependency_edges<'a> = stream![VersionId => VersionId; isize];\n    let direct_dependency_edges: dependency_edges = dependencies\n        .map(|dep| ((dep.crate_id, dep.req), dep.version_id))\n        .KV::<(CrateId, VersionReq), VersionId>()\n        .join_core(\n            &resolved,\n            |(_crate_id, _req), from_version_id, to_version_id| {\n                once((*from_version_id, *to_version_id))\n            },\n        );\n\n    // releases that are the most recent of their crate\n    type most_recent_crate_version<'a> = stream![VersionId; isize];\n    let most_recent_crate_version: most_recent_crate_version = releases\n        .map(|rel| {\n            (\n                rel.crate_id,\n                (rel.num.pre.is_empty(), rel.created_at, rel.id),\n            )\n        })\n        .KV::<CrateId, (bool, DateTime, VersionId)>()\n        .max_by_key()\n        .KV::<CrateId, (bool, DateTime, VersionId)>()\n        .map(|(_crate_id, (_not_prerelease, _created_at, version_id))| version_id);\n    let most_recent_crate_version = most_recent_crate_version.arrange_by_self();\n\n    // releases that satisfy the predicate of each query\n    type match_releases<'a> = stream![VersionId => QueryId; Present];\n    let match_releases: match_releases = queries\n        .flat_map(|query| {\n            query\n                .predicates\n                .iter()\n                .map(move |pred| (pred.crate_id, (query.id, pred.req)))\n        })\n        .KV::<CrateId, (QueryId, Option<VersionReq>)>()\n        .join_core(\n            &releases_by_crate_id,\n            |_crate_id, (query_id, version_req), (version_id, version)| {\n                let matches = match version_req {\n                    None => true,\n                    Some(req) => req.matches(version),\n                };\n                matches.then_some((*version_id, *query_id))\n            },\n        );\n\n    // releases that contribute into the result of each query\n    type query_results<'a> = stream![VersionId => QueryId; isize];\n    let mut query_results: query_results = direct_dependency_edges\n        .join_core(&most_recent_crate_version, |edge_from, edge_to, ()| {\n            once((*edge_to, *edge_from))\n        })\n        .KV::<VersionId, VersionId>()\n        .join_map(&match_releases, |_edge_to, edge_from, query_id| {\n            (*edge_from, *query_id)\n        });\n\n    if transitive {\n        type dependency_edges<'a> = stream![VersionFeature => VersionFeature; isize];\n\n        // dependency edges arising from an entry under [dependencies]\n        let dep_dependency_edges: dependency_edges = dependencies\n            .flat_map(|dep| match dep.kind {\n                DependencyKind::Normal | DependencyKind::Build => Some((\n                    (dep.crate_id, dep.req),\n                    (\n                        dep.version_id,\n                        dep.feature_id,\n                        dep.default_features,\n                        dep.features,\n                    ),\n                )),\n                DependencyKind::Dev => None,\n            })\n            .KV::<(CrateId, VersionReq), (VersionId, FeatureId, DefaultFeatures, Slice<FeatureId>)>(\n            )\n            .join_core(\n                &resolved,\n                |(_crate_id, _req),\n                 (version_id, feature_id, default_features, features),\n                 resolved_version_id| {\n                    let edge_from = VersionFeature {\n                        version_id: *version_id,\n                        feature_id: *feature_id,\n                    };\n                    let resolved_version_id = *resolved_version_id;\n                    FeatureIter::new(*default_features, *features).map(move |feature_id| {\n                        let edge_to = VersionFeature {\n                            version_id: resolved_version_id,\n                            feature_id,\n                        };\n                        (edge_from, edge_to)\n                    })\n                },\n            );\n\n        // dependency edges from crate feature enabling other feature of same crate\n        let feature_intracrate_edges: dependency_edges = releases.explode(|rel| {\n            let version_id = rel.id;\n            let crate_id = rel.crate_id;\n            rel.features\n                .iter()\n                .flat_map(move |feature| {\n                    let edge_from = VersionFeature {\n                        version_id,\n                        feature_id: feature.id,\n                    };\n                    feature\n                        .enables\n                        .into_iter()\n                        .filter_map(move |crate_feature| {\n                            if crate_feature.crate_id == crate_id {\n                                let edge_to = VersionFeature {\n                                    version_id,\n                                    feature_id: crate_feature.feature_id,\n                                };\n                                Some((edge_from, edge_to))\n                            } else {\n                                None\n                            }\n                        })\n                        .chain({\n                            if feature.id == FeatureId::DEFAULT {\n                                None\n                            } else {\n                                let edge_to = VersionFeature {\n                                    version_id,\n                                    feature_id: FeatureId::CRATE,\n                                };\n                                Some((edge_from, edge_to))\n                            }\n                        })\n                })\n                .chain({\n                    let edge_from = VersionFeature {\n                        version_id,\n                        feature_id: FeatureId::DEFAULT,\n                    };\n                    let edge_to = VersionFeature {\n                        version_id,\n                        feature_id: FeatureId::CRATE,\n                    };\n                    once((edge_from, edge_to))\n                })\n                .map(|(edge_from, edge_to)| ((edge_from, edge_to), 1))\n        });\n\n        // dependency edges from crate feature enabling feature of other crate\n        let feature_dependency_edges: dependency_edges = releases\n            .flat_map(|rel| {\n                let version_id = rel.id;\n                let crate_id = rel.crate_id;\n                rel.features.into_iter().flat_map(move |feature| {\n                    // TODO: also handle `weak_enables`\n                    // https://github.com/dtolnay/cargo-tally/issues/56\n                    feature\n                        .enables\n                        .into_iter()\n                        .filter_map(move |crate_feature| {\n                            if crate_feature.crate_id == crate_id {\n                                None\n                            } else {\n                                Some((\n                                    (version_id, crate_feature.crate_id),\n                                    (feature.id, crate_feature.feature_id),\n                                ))\n                            }\n                        })\n                })\n            })\n            .KV::<(VersionId, CrateId), (FeatureId, FeatureId)>()\n            .join_map(\n                &dependencies\n                    .map(|dep| ((dep.version_id, dep.crate_id), dep.req))\n                    .KV::<(VersionId, CrateId), VersionReq>(),\n                |(version_id, crate_id), (from_feature, to_feature), req| {\n                    ((*crate_id, *req), (*version_id, *from_feature, *to_feature))\n                },\n            )\n            .KV::<(CrateId, VersionReq), (VersionId, FeatureId, FeatureId)>()\n            .join_core(\n                &resolved,\n                |(_crate_id, _req),\n                 (from_version_id, from_feature_id, to_feature_id),\n                 to_version_id| {\n                    let edge_from = VersionFeature {\n                        version_id: *from_version_id,\n                        feature_id: *from_feature_id,\n                    };\n                    let edge_to = VersionFeature {\n                        version_id: *to_version_id,\n                        feature_id: *to_feature_id,\n                    };\n                    Some((edge_from, edge_to))\n                },\n            );\n\n        // full dependency graph across all versions of all crates\n        let incoming_transitive_dependency_edges = dep_dependency_edges\n            .concat(&feature_intracrate_edges)\n            .concat(&feature_dependency_edges)\n            .KV::<VersionFeature, VersionFeature>()\n            .map_in_place(|edge| {\n                let (edge_from, edge_to) = *edge;\n                *edge = (edge_to, edge_from);\n            })\n            .KV::<VersionFeature, VersionFeature>()\n            .arrange_by_key();\n\n        // fixed point of transitive dependencies graph\n        type addend_transitive_releases<'a> = stream![VersionId => QueryId; isize];\n        let addend_transitive_releases: addend_transitive_releases = scope\n            .iterative::<u16, _, _>(|nested| {\n                let match_releases = match_releases\n                    .KV::<VersionId, QueryId>()\n                    .explode(|(version_id, query_id)| {\n                        let version_feature = VersionFeature {\n                            version_id,\n                            feature_id: FeatureId::CRATE,\n                        };\n                        once(((version_feature, query_id), 1))\n                    })\n                    .KV::<VersionFeature, QueryId>()\n                    .enter(nested);\n                let summary = Product::new(Duration::default(), 1);\n                let variable = Variable::new_from(match_releases, summary);\n                let result = variable\n                    .deref()\n                    .KV::<VersionFeature, QueryId>()\n                    .join_core(\n                        &incoming_transitive_dependency_edges.enter(nested),\n                        |_edge_to, query_id, edge_from| Some((*edge_from, *query_id)),\n                    )\n                    .KV::<VersionFeature, QueryId>()\n                    .concat(&variable)\n                    .KV::<VersionFeature, QueryId>()\n                    .distinct();\n                variable.set(&result).leave()\n            })\n            .KV::<VersionFeature, QueryId>()\n            .map(|(version_feature, query_id)| (version_feature.version_id, query_id));\n\n        query_results = addend_transitive_releases\n            .join_core(&most_recent_crate_version, |version_id, query_id, ()| {\n                Some((*version_id, *query_id))\n            })\n            .KV::<VersionId, QueryId>()\n            .concat(&query_results);\n    }\n\n    query_results\n        .distinct()\n        .map(|(_version_id, query_id)| query_id)\n        .consolidate()\n        .collect_into(results);\n}\n"
  },
  {
    "path": "src/load.rs",
    "content": "use crate::cratemap::CrateMap;\nuse crate::user::User;\nuse anyhow::{bail, Result};\nuse cargo_tally::arena::Slice;\nuse cargo_tally::dependency::DependencyKind;\nuse cargo_tally::feature::{\n    CrateFeature, DefaultFeatures, FeatureEnables, FeatureId, FeatureNames,\n};\nuse cargo_tally::id::{CrateId, DependencyId, VersionId};\nuse cargo_tally::timestamp::DateTime;\nuse cargo_tally::version::{Version, VersionReq};\nuse cargo_tally::{DbDump, Dependency, Release};\nuse db_dump::crate_owners::OwnerId;\nuse std::cell::RefCell;\nuse std::collections::{BTreeMap as Map, BTreeSet as Set};\nuse std::mem;\nuse std::path::Path;\n\npub(crate) fn load(path: impl AsRef<Path>) -> Result<(DbDump, CrateMap)> {\n    let mut crates = CrateMap::new();\n    let mut users: Map<User, OwnerId> = Map::new();\n    let mut teams: Map<User, OwnerId> = Map::new();\n    let mut owners: Map<OwnerId, Vec<CrateId>> = Map::new();\n    let mut releases: Vec<Release> = Vec::new();\n    let mut dependencies: Vec<Dependency> = Vec::new();\n    let mut release_features: Vec<Vec<(FeatureId, Vec<CrateFeature>, Vec<CrateFeature>)>> =\n        Vec::new();\n    let mut dep_renames: Map<DependencyId, String> = Map::new();\n    let mut dep_renames_resolve: Map<(VersionId, FeatureId), CrateId> = Map::new();\n    let feature_names = RefCell::new(FeatureNames::new());\n\n    db_dump::Loader::new()\n        .crates(|row| {\n            let crate_id = CrateId::from(row.id);\n            crates.insert(crate_id, row.name);\n        })\n        .users(|row| {\n            users.insert(User::new(row.gh_login), OwnerId::User(row.id));\n        })\n        .teams(|row| {\n            if let Some(team) = row.login.strip_prefix(\"github:\") {\n                if team.contains(':') {\n                    let team = team.replace(':', \"/\");\n                    teams.insert(User::new(team), OwnerId::Team(row.id));\n                }\n            }\n        })\n        .crate_owners(|row| {\n            owners\n                .entry(row.owner_id)\n                .or_insert_with(Vec::new)\n                .push(CrateId::from(row.crate_id));\n        })\n        .versions(|row| {\n            if row.yanked {\n                return;\n            }\n            let crate_id = CrateId::from(row.crate_id);\n            let mut features = Vec::new();\n            if !row.features.is_empty() {\n                let mut feature_names = feature_names.borrow_mut();\n                for (feature, raw_enables) in &row.features {\n                    let feature_id = feature_names.id(feature);\n                    let mut enables = Vec::new();\n                    let mut weak_enables = Vec::new();\n                    for feature in raw_enables {\n                        let crate_id;\n                        let mut crate_feature_vec = &mut enables;\n                        let mut feature = feature.as_str();\n                        if let Some(slash) = feature.find('/') {\n                            let mut crate_name = &feature[..slash];\n                            if let Some(crate_name_weak) = crate_name.strip_suffix('?') {\n                                crate_name = crate_name_weak;\n                                crate_feature_vec = &mut weak_enables;\n                            }\n                            crate_id = feature_names.id(crate_name);\n                            feature = &feature[slash + 1..];\n                        } else {\n                            crate_id = FeatureId::CRATE;\n                        }\n                        let feature_id = feature_names.id(feature);\n                        crate_feature_vec.push(CrateFeature {\n                            crate_id: CrateId(crate_id.0),\n                            feature_id,\n                        });\n                    }\n                    features.push((feature_id, enables, weak_enables));\n                }\n            }\n            releases.push(Release {\n                id: VersionId::from(row.id),\n                crate_id,\n                num: Version(row.num),\n                created_at: DateTime::from(row.created_at),\n                features: {\n                    release_features.push(features);\n                    Slice::EMPTY\n                },\n            });\n        })\n        .dependencies(|row| {\n            let dependency_id = DependencyId::from(row.id);\n            let version_id = VersionId::from(row.version_id);\n            let crate_id = CrateId::from(row.crate_id);\n            let feature_id = if row.optional {\n                FeatureId::TBD\n            } else {\n                FeatureId::CRATE\n            };\n            let mut default_features = row.default_features;\n            let mut features = Set::new();\n            if !row.features.is_empty() {\n                let mut feature_names = feature_names.borrow_mut();\n                for feature in &row.features {\n                    let feature_id = feature_names.id(feature);\n                    if feature_id == FeatureId::DEFAULT {\n                        default_features = true;\n                    } else {\n                        features.insert(feature_id);\n                    }\n                }\n            }\n            if let Some(explicit_name) = row.explicit_name {\n                let mut feature_names = feature_names.borrow_mut();\n                dep_renames_resolve\n                    .insert((version_id, feature_names.id(&explicit_name)), crate_id);\n                dep_renames.insert(dependency_id, explicit_name);\n            }\n            dependencies.push(Dependency {\n                id: dependency_id,\n                version_id,\n                crate_id,\n                req: VersionReq::from(row.req),\n                feature_id,\n                default_features: DefaultFeatures(default_features),\n                features: Slice::from_iter(features),\n                kind: DependencyKind::from(row.kind),\n            });\n        })\n        .load(path)?;\n\n    crate::mend::mend_crates(&mut crates);\n\n    let known_broken = [(crates.id(\"modbus\"), &Version::new(0, 1, 0), \"test-server\")];\n\n    let mut feature_names = mem::take(&mut *feature_names.borrow_mut());\n    let mut feature_buffer = Vec::new();\n    for (release, mut features) in releases.iter_mut().zip(release_features) {\n        for (feature, enables, weak_enables) in &mut features {\n            for crate_features in [&mut *enables, &mut *weak_enables] {\n                for feature in crate_features {\n                    let feature_id = FeatureId(feature.crate_id.0);\n                    feature.crate_id = if feature_id == FeatureId::CRATE {\n                        release.crate_id\n                    } else if let Some(crate_id) =\n                        dep_renames_resolve.get(&(release.id, feature_id))\n                    {\n                        *crate_id\n                    } else if let Some(crate_id) = {\n                        let name = feature_names.name(feature_id);\n                        crates.id(name)\n                    } {\n                        crate_id\n                    } else if known_broken.contains(&(\n                        Some(release.crate_id),\n                        &release.num,\n                        feature_names.name(feature_id),\n                    )) {\n                        release.crate_id\n                    } else {\n                        bail!(\n                            \"{} v{} depends on {} which is not found\",\n                            crates.name(release.crate_id).unwrap(),\n                            release.num,\n                            feature_names.name(feature_id),\n                        );\n                    };\n                }\n            }\n            feature_buffer.push(FeatureEnables {\n                id: *feature,\n                enables: Slice::new(enables),\n                weak_enables: Slice::new(weak_enables),\n            });\n        }\n        release.features = Slice::new(&feature_buffer);\n        feature_buffer.clear();\n    }\n    for dep in &mut dependencies {\n        if dep.feature_id == FeatureId::TBD {\n            dep.feature_id = feature_names.id(match dep_renames.get(&dep.id) {\n                Some(explicit_name) => explicit_name,\n                None => crates.name(dep.crate_id).unwrap(),\n            });\n        }\n    }\n\n    let mut db_dump = DbDump {\n        releases,\n        dependencies,\n        features: feature_names,\n    };\n\n    crates.owners = owners;\n    crates.users = users;\n    crates.users.extend(teams);\n\n    crate::mend::mend_releases(&mut db_dump, &crates);\n\n    Ok((db_dump, crates))\n}\n"
  },
  {
    "path": "src/log.rs",
    "content": "use std::fmt;\nuse std::io::Write;\nuse termcolor::{Color, ColorSpec, StandardStream, WriteColor};\n\npub trait Log {\n    fn trace(&mut self) -> LogStream;\n    fn warning(&mut self) -> LogStream;\n    fn error(&mut self) -> LogStream;\n    fn red(&mut self) -> LogStream;\n}\n\nimpl Log for StandardStream {\n    fn trace(&mut self) -> LogStream {\n        let mut color = ColorSpec::new();\n        color.set_fg(Some(Color::Magenta)).set_dimmed(true);\n        let _ = self.set_color(&color);\n        LogStream(self)\n    }\n\n    fn warning(&mut self) -> LogStream {\n        let mut color = ColorSpec::new();\n        color.set_fg(Some(Color::Yellow));\n        let _ = self.set_color(&color);\n        LogStream(self)\n    }\n\n    fn error(&mut self) -> LogStream {\n        let mut color = ColorSpec::new();\n        color.set_fg(Some(Color::Red)).set_bold(true);\n        let _ = self.set_color(&color);\n        let _ = write!(self, \"error:\");\n        let _ = self.reset();\n        let _ = write!(self, \" \");\n        LogStream(self)\n    }\n\n    fn red(&mut self) -> LogStream {\n        let mut color = ColorSpec::new();\n        color.set_fg(Some(Color::Red));\n        let _ = self.set_color(&color);\n        LogStream(self)\n    }\n}\n\npub struct LogStream<'a>(&'a mut StandardStream);\n\nimpl<'a> LogStream<'a> {\n    pub fn write_fmt(&mut self, args: fmt::Arguments) {\n        let _ = self.0.write_fmt(args);\n    }\n}\n\nimpl<'a> Drop for LogStream<'a> {\n    fn drop(&mut self) {\n        let _ = self.0.reset();\n    }\n}\n"
  },
  {
    "path": "src/macros.rs",
    "content": "macro_rules! const_assert_eq {\n    ($left:expr, $right:expr) => {\n        const _: [(); $left as usize] = [(); $right as usize];\n    };\n}\n\nmacro_rules! const_assert {\n    ($($cond:expr),* $(,)?) => {\n        const_assert_eq!($($cond)&&*, true);\n    };\n}\n\nmacro_rules! version {\n    ($major_minor:tt . $patch:tt) => {{\n        const major_minor: &'static [u8] = stringify!($major_minor).as_bytes();\n        const_assert! {\n            major_minor.len() == 3,\n            major_minor[0] >= b'0' && major_minor[0] <= b'9',\n            major_minor[1] == b'.',\n            major_minor[2] >= b'0' && major_minor[2] <= b'9',\n        }\n        cargo_tally::version::Version(semver::Version {\n            major: (major_minor[0] - b'0') as u64,\n            minor: (major_minor[2] - b'0') as u64,\n            patch: $patch,\n            pre: semver::Prerelease::EMPTY,\n            build: semver::BuildMetadata::EMPTY,\n        })\n    }};\n}\n\nmacro_rules! version_req {\n    (^ $major_minor:tt) => {{\n        const major_minor: &'static [u8] = stringify!($major_minor).as_bytes();\n        const_assert! {\n            major_minor.len() == 3,\n            major_minor[0] >= b'0' && major_minor[0] <= b'9',\n            major_minor[1] == b'.',\n            major_minor[2] >= b'0' && major_minor[2] <= b'9',\n        }\n        const comparators: &'static [semver::Comparator] = &[semver::Comparator {\n            op: semver::Op::Caret,\n            major: (major_minor[0] - b'0') as u64,\n            minor: Some((major_minor[2] - b'0') as u64),\n            patch: None,\n            pre: semver::Prerelease::EMPTY,\n        }];\n        cargo_tally::version::VersionReq {\n            comparators: cargo_tally::arena::Slice::from(comparators),\n        }\n    }};\n}\n\nmacro_rules! datetime {\n    ($day:tt $month:ident $year:tt $hour:tt : $min:tt : $sec:tt) => {{\n        const_assert! {\n            $day >= 1 && $day <= 31,\n            $year >= 2014,\n            $hour >= 0 && $hour <= 23,\n            $min >= 0 && $min <= 59,\n            $sec >= 0 && $sec <= 60,\n        }\n        cargo_tally::timestamp::DateTime::new(\n            chrono::NaiveDate::from_ymd_opt($year, month_number!($month), $day).unwrap(),\n            chrono::NaiveTime::from_hms_opt($hour, $min, $sec).unwrap(),\n        )\n    }};\n}\n\n#[rustfmt::skip]\n#[allow(unknown_lints, unused_macro_rules)]\nmacro_rules! month_number {\n    (Jan) => { 1 };\n    (Feb) => { 2 };\n    (Mar) => { 3 };\n    (Apr) => { 4 };\n    (May) => { 5 };\n    (Jun) => { 6 };\n    (Jul) => { 7 };\n    (Aug) => { 8 };\n    (Sep) => { 9 };\n    (Oct) => { 10 };\n    (Nov) => { 11 };\n    (Dec) => { 12 };\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![deny(unsafe_op_in_unsafe_fn)]\n#![allow(non_upper_case_globals)]\n#![allow(\n    clippy::cast_lossless,\n    clippy::cast_possible_truncation,\n    clippy::cast_precision_loss,\n    clippy::collapsible_else_if,\n    clippy::elidable_lifetime_names,\n    clippy::expl_impl_clone_on_copy,\n    clippy::let_underscore_untyped,\n    clippy::manual_range_contains,\n    clippy::map_clone,\n    clippy::module_name_repetitions,\n    clippy::needless_lifetimes,\n    clippy::redundant_else,\n    clippy::single_match_else,\n    clippy::too_many_lines,\n    clippy::type_complexity,\n    clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133\n    clippy::uninlined_format_args,\n    clippy::unwrap_or_default,\n    clippy::zero_prefixed_literal\n)]\n#![allow(unknown_lints, mismatched_lifetime_syntaxes)]\n\n#[macro_use]\nmod macros;\n\nmod alloc;\nmod args;\nmod clean;\nmod cratemap;\nmod cratename;\nmod filter;\nmod load;\nmod log;\nmod mend;\nmod query;\nmod render;\nmod total;\nmod trace;\nmod user;\n\nuse crate::load::load;\nuse crate::log::Log;\nuse crate::total::Total;\nuse anyhow::Result;\nuse std::io::{self, IsTerminal, Write};\nuse std::process;\nuse std::time::Instant;\nuse termcolor::{ColorChoice, StandardStream};\n\ncargo_subcommand_metadata::description!(\n    \"Draw graphs of the number of dependencies on a crate over time\"\n);\n\nfn main() {\n    let mut stderr = StandardStream::stderr(ColorChoice::Auto);\n    if let Err(err) = try_main(&mut stderr) {\n        writeln!(stderr.error(), \"{}\", err);\n        process::exit(1);\n    }\n}\n\nfn try_main(stderr: &mut StandardStream) -> Result<()> {\n    let opt = args::parse();\n\n    if !opt.db.is_file() {\n        write!(stderr.error(), \"Database dump file does not exist: \");\n        write!(stderr.red(), \"{}\", opt.db.display());\n        let _ = writeln!(\n            stderr,\n            \"\\nDownload one from https://static.crates.io/db-dump.tar.gz\",\n        );\n        process::exit(1);\n    }\n\n    let mut sysinfo = sysinfo::System::new();\n    sysinfo.refresh_memory();\n    let total_memory = sysinfo.total_memory();\n    let (min_memory, advised) = if opt.transitive {\n        (10 * 1024 * 1024 * 1024, \"12 GB\")\n    } else {\n        (7 * 1024 * 1024 * 1024, \"8 GB\")\n    };\n    if total_memory < min_memory && total_memory > 0 {\n        writeln!(\n            stderr.warning(),\n            \"warning: running with <{advised} memory is not advised.\",\n        );\n    }\n\n    let stdout_isatty = io::stdout().is_terminal();\n    let stderr_isatty = io::stderr().is_terminal();\n\n    let instant = Instant::now();\n    let (mut db_dump, crates) = crate::load(&opt.db)?;\n    crate::filter::filter(&mut db_dump, &crates, &opt.exclude);\n    db_dump.releases.sort_by_key(|v| v.created_at);\n    crate::clean::clean(&mut db_dump, &crates);\n    let total = opt.relative.then(|| Total::index(&db_dump.releases));\n    if stderr_isatty {\n        writeln!(stderr.trace(), \"load time: {:.2?}\", instant.elapsed());\n    }\n\n    let query_strings = opt.queries.iter().map(String::as_str);\n    let queries = query::parse(query_strings, &crates)?;\n    let instant = Instant::now();\n    let results = cargo_tally::run(db_dump, opt.jobs, opt.transitive, &queries);\n    if stderr_isatty {\n        writeln!(stderr.trace(), \"dataflow time: {:.2?}\", instant.elapsed());\n    }\n\n    let _ = stderr.flush();\n    let len = results.len();\n    let stdout = io::stdout();\n    let mut stdout = stdout.lock();\n    for (i, (timestamp, data)) in results.iter().enumerate() {\n        if stdout_isatty && 10 + i == len && len > 20 {\n            let _ = writeln!(stdout, \"...\");\n        }\n        if !stdout_isatty || i < 10 || 10 + i >= len {\n            if let Some(total) = &total {\n                let total = total.eval(timestamp);\n                let _ = writeln!(stdout, \"{:?} {:?}\", timestamp, data / total);\n            } else {\n                let _ = writeln!(stdout, \"{:?} {:?}\", timestamp, data);\n            }\n        }\n    }\n    let _ = stdout.flush();\n\n    let graph_path = if stdout_isatty {\n        if results.is_empty() {\n            writeln!(stderr.red(), \"zero results\");\n            None\n        } else {\n            let labels = opt\n                .queries\n                .iter()\n                .map(|query| query::format(query, &crates))\n                .collect::<Vec<_>>();\n            let graph_path = render::graph(\n                opt.title.as_deref(),\n                opt.transitive,\n                &results,\n                &labels,\n                total.as_ref(),\n            )?;\n            Some(graph_path)\n        }\n    } else {\n        None\n    };\n\n    if stderr_isatty {\n        writeln!(stderr.trace(), \"{}\", alloc::stat());\n    }\n\n    if let Some(path) = graph_path {\n        writeln!(stderr.trace(), \"graph written to {}\", path.display());\n        let _ = opener::open(&path);\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/matrix.rs",
    "content": "use crate::timestamp::DateTime;\nuse ref_cast::RefCast;\nuse std::fmt::{self, Debug};\nuse std::iter::Copied;\nuse std::ops::{Deref, Div, Index};\nuse std::slice;\n\npub struct Matrix {\n    queries: usize,\n    rows: Vec<(DateTime, Vec<u32>)>,\n}\n\n#[derive(RefCast)]\n#[repr(transparent)]\npub struct Row([u32]);\n\nimpl Matrix {\n    pub(crate) fn new(queries: usize) -> Self {\n        Matrix {\n            queries,\n            rows: Vec::new(),\n        }\n    }\n\n    pub fn width(&self) -> usize {\n        self.queries\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.rows.is_empty()\n    }\n\n    pub fn len(&self) -> usize {\n        self.rows.len()\n    }\n\n    pub fn iter(&self) -> Iter {\n        Iter(self.rows.iter())\n    }\n\n    pub(crate) fn push(&mut self, timestamp: DateTime, data: Vec<u32>) {\n        self.rows.push((timestamp, data));\n    }\n}\n\nimpl<'a> IntoIterator for &'a Matrix {\n    type Item = (DateTime, &'a Row);\n    type IntoIter = Iter<'a>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\n\npub struct Iter<'a>(slice::Iter<'a, (DateTime, Vec<u32>)>);\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = (DateTime, &'a Row);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0\n            .next()\n            .map(|(timestamp, data)| (*timestamp, Row::ref_cast(data)))\n    }\n}\n\nimpl<'a> DoubleEndedIterator for Iter<'a> {\n    fn next_back(&mut self) -> Option<Self::Item> {\n        self.0\n            .next_back()\n            .map(|(timestamp, data)| (*timestamp, Row::ref_cast(data)))\n    }\n}\n\nimpl Index<usize> for Row {\n    type Output = u32;\n\n    fn index(&self, i: usize) -> &Self::Output {\n        &self.0[i]\n    }\n}\n\nimpl<'a> IntoIterator for &'a Row {\n    type Item = u32;\n    type IntoIter = Copied<slice::Iter<'a, u32>>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.iter().copied()\n    }\n}\n\nimpl Deref for Row {\n    type Target = [u32];\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\npub struct RelativeRow<'a> {\n    row: &'a Row,\n    total: u32,\n}\n\nimpl<'a> Div<u32> for &'a Row {\n    type Output = RelativeRow<'a>;\n\n    fn div(self, rhs: u32) -> Self::Output {\n        RelativeRow {\n            row: self,\n            total: rhs,\n        }\n    }\n}\n\nimpl Debug for Row {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        formatter.debug_list().entries(&self.0).finish()\n    }\n}\n\nimpl<'a> Debug for RelativeRow<'a> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        let mut list = formatter.debug_list();\n        for value in self.row {\n            list.entry(&(value as f32 / self.total as f32));\n        }\n        list.finish()\n    }\n}\n"
  },
  {
    "path": "src/max.rs",
    "content": "use crate::hint::TypeHint;\nuse crate::present::Present;\nuse differential_dataflow::collection::Collection;\nuse differential_dataflow::difference::{Multiply, Semigroup};\nuse differential_dataflow::lattice::Lattice;\nuse differential_dataflow::operators::CountTotal;\nuse differential_dataflow::ExchangeData;\nuse std::fmt::Debug;\nuse std::hash::Hash;\nuse std::iter::once;\nuse timely::dataflow::Scope;\nuse timely::order::TotalOrder;\n\npub(crate) trait MaxByKey<G, K, V, R>\nwhere\n    G: Scope,\n{\n    fn max_by_key(&self) -> Collection<G, (K, V), isize>;\n}\n\nimpl<G, K, V, R> MaxByKey<G, K, V, R> for Collection<G, (K, V), R>\nwhere\n    G: Scope,\n    K: Clone + ExchangeData + Hash,\n    V: Clone + Ord + ExchangeData + Debug,\n    R: Semigroup,\n    Max<V>: Multiply<R, Output = Max<V>>,\n    G::Timestamp: TotalOrder + Lattice,\n{\n    fn max_by_key(&self) -> Collection<G, (K, V), isize> {\n        self.explode(|(key, value)| once((key, Max { value })))\n            .T::<K>()\n            .count_total()\n            .KV::<K, Max<V>>()\n            .map(|(key, max)| (key, max.value))\n    }\n}\n\n#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]\npub(crate) struct Max<T> {\n    value: T,\n}\n\nimpl<T> Multiply<Present> for Max<T> {\n    type Output = Self;\n\n    fn multiply(self, rhs: &Present) -> Self::Output {\n        let _ = rhs;\n        self\n    }\n}\n\nimpl<T> Semigroup for Max<T>\nwhere\n    T: Ord + Clone + Debug + 'static,\n{\n    fn plus_equals(&mut self, rhs: &Self) {\n        if self.value < rhs.value {\n            self.value = rhs.value.clone();\n        }\n    }\n\n    fn is_zero(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "src/mend.rs",
    "content": "//! Fill back in some deleted releases that cause nontrivial number of\n//! dependencies downstream to fail to resolve.\n\nuse crate::cratemap::CrateMap;\nuse cargo_tally::arena::Slice;\nuse cargo_tally::dependency::DependencyKind;\nuse cargo_tally::feature::{CrateFeature, DefaultFeatures, FeatureEnables, FeatureId};\nuse cargo_tally::id::{CrateId, DependencyId, VersionId};\nuse cargo_tally::{DbDump, Dependency, Release};\nuse std::collections::BTreeSet as Set;\n\npub(crate) fn mend_crates(crates: &mut CrateMap) {\n    let mut next_crate_id = CrateId(1);\n\n    for crate_name in [\n        \"futures\",\n        \"git-version\",\n        \"lazy_static\",\n        \"partial-io\",\n        \"quickcheck\",\n        \"tokio-core\",\n        \"tokio-io\",\n        \"vela-utils\",\n        \"xcm\",\n        \"xcm-executor\",\n    ] {\n        if crates.id(crate_name).is_none() {\n            while crates.name(next_crate_id).is_some() {\n                next_crate_id.0 += 1;\n            }\n            crates.insert(next_crate_id, crate_name.to_owned());\n        }\n    }\n}\n\npub(crate) fn mend_releases(db_dump: &mut DbDump, crates: &CrateMap) {\n    let mut used_version_ids = Set::new();\n    let mut used_version_numbers = Set::new();\n    for rel in &db_dump.releases {\n        used_version_ids.insert(rel.id);\n        used_version_numbers.insert((rel.crate_id, rel.num.clone()));\n    }\n\n    let mut used_dependency_ids = Set::new();\n    for dep in &db_dump.dependencies {\n        used_dependency_ids.insert(dep.id);\n    }\n\n    let mut next_version_id = VersionId(0);\n    let mut next_version_id = || {\n        while !used_version_ids.insert(next_version_id) {\n            next_version_id.0 += 1;\n        }\n        next_version_id\n    };\n\n    let mut next_dependency_id = DependencyId(0);\n    let mut next_dependency_id = || {\n        while !used_dependency_ids.insert(next_dependency_id) {\n            next_dependency_id.0 += 1;\n        }\n        next_dependency_id\n    };\n\n    let releases = &mut db_dump.releases;\n    let mut push_release = |rel: Release| {\n        assert!(used_version_numbers.insert((rel.crate_id, rel.num.clone())));\n        releases.push(rel);\n    };\n\n    {\n        let crate_id = crates.id(\"git-version\").unwrap();\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.1.0),\n            created_at: datetime!(18 Oct 2017 13:53:11),\n            features: Slice::EMPTY,\n        });\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.1.1),\n            created_at: datetime!(18 Oct 2017 13:55:40),\n            features: Slice::EMPTY,\n        });\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.1.2),\n            created_at: datetime!(18 Oct 2017 13:57:15),\n            features: Slice::EMPTY,\n        });\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.2.0),\n            created_at: datetime!(5 Apr 2018 09:14:16),\n            features: Slice::EMPTY,\n        });\n    }\n\n    {\n        let crate_id = crates.id(\"partial-io\").unwrap();\n\n        let features = Slice::new(&[FeatureEnables {\n            id: db_dump.features.id(\"tokio\"),\n            enables: Slice::new(&[\n                CrateFeature {\n                    crate_id,\n                    feature_id: db_dump.features.id(\"tokio-io\"),\n                },\n                CrateFeature {\n                    crate_id,\n                    feature_id: db_dump.features.id(\"futures\"),\n                },\n            ]),\n            weak_enables: Slice::new(&[]),\n        }]);\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.1.0),\n                created_at: datetime!(26 May 2017 02:38:58),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.1.1),\n                created_at: datetime!(27 May 2017 00:56:37),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.0),\n                created_at: datetime!(30 May 2017 21:01:28),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.1),\n                created_at: datetime!(30 May 2017 21:47:41),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.2),\n                created_at: datetime!(12 Jun 2017 05:26:52),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.3),\n                created_at: datetime!(20 Jul 2017 20:01:22),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.4),\n                created_at: datetime!(19 Aug 2017 23:37:51),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.2.5),\n                created_at: datetime!(18 Nov 2017 02:26:25),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^0.2),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.4),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n\n        push_release({\n            let release = Release {\n                id: next_version_id(),\n                crate_id,\n                num: version!(0.3.0),\n                created_at: datetime!(12 Jan 2018 22:15:15),\n                features,\n            };\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"futures\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"futures\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"lazy_static\").unwrap(),\n                req: version_req!(^1.0),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.6),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"quickcheck\").unwrap(),\n                req: version_req!(^0.6),\n                feature_id: db_dump.features.id(\"quickcheck\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-core\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: FeatureId::CRATE,\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Dev,\n            });\n            db_dump.dependencies.push(Dependency {\n                id: next_dependency_id(),\n                version_id: release.id,\n                crate_id: crates.id(\"tokio-io\").unwrap(),\n                req: version_req!(^0.1),\n                feature_id: db_dump.features.id(\"tokio-io\"),\n                default_features: DefaultFeatures(true),\n                features: Slice::EMPTY,\n                kind: DependencyKind::Normal,\n            });\n            release\n        });\n    }\n\n    {\n        let crate_id = crates.id(\"xcm\").unwrap();\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.0.0),\n            created_at: datetime!(9 Mar 2021 05:51:34),\n            features: Slice::EMPTY,\n        });\n    }\n\n    {\n        let crate_id = crates.id(\"xcm-executor\").unwrap();\n\n        push_release(Release {\n            id: next_version_id(),\n            crate_id,\n            num: version!(0.0.0),\n            created_at: datetime!(9 Mar 2021 06:21:39),\n            features: Slice::EMPTY,\n        });\n    }\n}\n"
  },
  {
    "path": "src/present.rs",
    "content": "use differential_dataflow::difference::{Multiply, Semigroup};\n\n#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]\npub(crate) struct Present;\n\nimpl Semigroup for Present {\n    fn plus_equals(&mut self, rhs: &Present) {\n        let _ = rhs;\n    }\n\n    fn is_zero(&self) -> bool {\n        false\n    }\n}\n\nimpl Multiply<Present> for Present {\n    type Output = Present;\n\n    fn multiply(self, rhs: &Present) -> Self::Output {\n        let _ = rhs;\n        Present\n    }\n}\n\nimpl Multiply<Present> for isize {\n    type Output = isize;\n\n    fn multiply(self, rhs: &Present) -> Self::Output {\n        let _ = rhs;\n        self\n    }\n}\n\nimpl Multiply<isize> for Present {\n    type Output = isize;\n\n    fn multiply(self, rhs: &isize) -> Self::Output {\n        *rhs\n    }\n}\n"
  },
  {
    "path": "src/query.rs",
    "content": "use crate::cratemap::CrateMap;\nuse crate::user::UserQuery;\nuse anyhow::{bail, format_err, Error, Result};\nuse cargo_tally::arena::Slice;\nuse cargo_tally::id::QueryId;\nuse cargo_tally::version::VersionReq;\nuse cargo_tally::{Predicate, Query};\nuse ref_cast::RefCast;\nuse std::fmt::{self, Display};\nuse std::str::{FromStr, Split};\n\n// for example &[\"serde:1.0\", \"anyhow:^1.0 + thiserror\"]\npub fn parse<'a>(\n    queries: impl IntoIterator<Item = &'a str>,\n    crates: &CrateMap,\n) -> Result<Vec<Query>> {\n    queries\n        .into_iter()\n        .enumerate()\n        .map(|(i, query)| {\n            let id = QueryId(u8::try_from(i).unwrap());\n            match parse_predicates(query, crates) {\n                Ok(predicates) => Ok(Query { id, predicates }),\n                Err(err) => bail!(\"failed to parse query {:?}: {}\", query, err),\n            }\n        })\n        .collect()\n}\n\nfn parse_predicates(string: &str, crates: &CrateMap) -> Result<Slice<Predicate>> {\n    let mut predicates = Vec::new();\n\n    for predicate in IterPredicates::new(string, crates) {\n        let predicate = predicate?;\n        match predicate {\n            RawPredicate::Crate(predicate) => predicates.push(predicate),\n            RawPredicate::User(username) => {\n                let Some(user_id) = crates.users.get(username) else {\n                    let kind = if username.is_team() { \"team\" } else { \"user\" };\n                    bail!(\"no crates owned by {} @{}\", kind, username);\n                };\n                predicates.extend(\n                    crates\n                        .owners\n                        .get(user_id)\n                        .map(Vec::as_slice)\n                        .unwrap_or_default()\n                        .iter()\n                        .map(|&crate_id| Predicate {\n                            crate_id,\n                            req: None,\n                        }),\n                );\n            }\n        }\n    }\n\n    Ok(Slice::new(&predicates))\n}\n\npub fn format(query: &str, crates: &CrateMap) -> String {\n    DisplayQuery { query, crates }.to_string()\n}\n\nstruct DisplayQuery<'a> {\n    query: &'a str,\n    crates: &'a CrateMap,\n}\n\nimpl<'a> Display for DisplayQuery<'a> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        for (i, predicate) in IterPredicates::new(self.query, self.crates).enumerate() {\n            if i > 0 {\n                formatter.write_str(\" or \")?;\n            }\n\n            let predicate = predicate.unwrap();\n            match predicate {\n                RawPredicate::Crate(predicate) => {\n                    let original_name = self.crates.name(predicate.crate_id).unwrap();\n                    formatter.write_str(original_name)?;\n                    if let Some(req) = predicate.req {\n                        write!(formatter, \":{}\", req)?;\n                    }\n                }\n                RawPredicate::User(username) => {\n                    let (username, _user_id) = self.crates.users.get_key_value(username).unwrap();\n                    write!(formatter, \"@{}\", username)?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\nenum RawPredicate<'a> {\n    Crate(Predicate),\n    User(&'a UserQuery),\n}\n\nstruct IterPredicates<'a> {\n    split: Split<'a, char>,\n    crates: &'a CrateMap,\n}\n\nimpl<'a> IterPredicates<'a> {\n    fn new(query: &'a str, crates: &'a CrateMap) -> Self {\n        IterPredicates {\n            split: query.split('+'),\n            crates,\n        }\n    }\n}\n\nimpl<'a> Iterator for IterPredicates<'a> {\n    type Item = Result<RawPredicate<'a>>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let predicate = self.split.next()?.trim();\n\n        if let Some(username) = predicate.strip_prefix('@') {\n            return Some(Ok(RawPredicate::User(UserQuery::ref_cast(username))));\n        }\n\n        let (name, req) = if let Some((name, req)) = predicate.split_once(':') {\n            match VersionReq::from_str(req) {\n                Ok(req) => (name, Some(req)),\n                Err(err) => return Some(Err(Error::new(err))),\n            }\n        } else {\n            (predicate, None)\n        };\n\n        let Some(crate_id) = self.crates.id(name) else {\n            return Some(Err(format_err!(\"no crate named {}\", name)));\n        };\n\n        Some(Ok(RawPredicate::Crate(Predicate { crate_id, req })))\n    }\n}\n"
  },
  {
    "path": "src/render.rs",
    "content": "use crate::total::Total;\nuse anyhow::Result;\nuse cargo_tally::matrix::Matrix;\nuse cargo_tally::timestamp::DateTime;\nuse std::cmp;\nuse std::env;\nuse std::fmt::{self, Display};\nuse std::fs;\nuse std::path::PathBuf;\n\npub(crate) fn graph(\n    title: Option<&str>,\n    transitive: bool,\n    results: &Matrix,\n    labels: &[String],\n    total: Option<&Total>,\n) -> Result<PathBuf> {\n    let now = DateTime::now();\n\n    let relative = total.is_some();\n    let title = if let Some(title) = title {\n        title\n    } else if relative {\n        if transitive {\n            \"fraction of crates.io depending transitively\"\n        } else {\n            \"fraction of crates.io depending directly\"\n        }\n    } else {\n        if transitive {\n            \"number of crates depending transitively\"\n        } else {\n            \"number of crates depending directly\"\n        }\n    };\n\n    let mut data = String::new();\n    data += \"[\\n\";\n    for (i, label) in labels.iter().enumerate() {\n        data += \"      {\\\"name\\\":\\\"\";\n        data += label;\n        data += \"\\\", \\\"values\\\":[\\n\";\n        let mut prev = None;\n        for (timestamp, row) in results {\n            let value = row[i];\n            if prev.is_none() {\n                if value == 0 {\n                    continue;\n                }\n                let mut secs = timestamp.seconds();\n                if timestamp.subsec_nanos() == 0 {\n                    secs = secs.saturating_sub(1);\n                }\n                let timestamp = DateTime::from_timestamp(secs, 0);\n                data += &Row(timestamp, 0, total).to_string();\n            } else if prev == Some(value) {\n                continue;\n            }\n            data += &Row(timestamp, value, total).to_string();\n            prev = Some(value);\n        }\n        let (timestamp, last) = results.iter().next_back().unwrap();\n        if timestamp < now {\n            data += &Row(now, last[i], total).to_string();\n        }\n        data += \"      ]},\\n\";\n    }\n    data += \"    ]\";\n\n    let template = include_str!(\"index.html\");\n    let mut preprocessor_context = minipre::Context::new();\n    preprocessor_context\n        .define(\"CARGO_TALLY_TITLE\", format!(\"\\\"{}\\\"\", title.escape_debug()))\n        .define(\"CARGO_TALLY_DATA\", data)\n        .define(\"CARGO_TALLY_RELATIVE\", (relative as usize).to_string());\n    let html = minipre::process_str(template, &mut preprocessor_context)?;\n\n    let dir = env::temp_dir().join(\"cargo-tally\");\n    fs::create_dir_all(&dir)?;\n    let path = dir.join(format!(\"{}.html\", now.millis()));\n    fs::write(&path, html)?;\n    Ok(path)\n}\n\nstruct Row<'a>(DateTime, u32, Option<&'a Total>);\n\nimpl<'a> Display for Row<'a> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        formatter.write_str(\"        {\\\"time\\\":\")?;\n        write!(formatter, \"{}\", self.0.millis())?;\n        formatter.write_str(\", \\\"edges\\\":\")?;\n        if let Some(total) = self.2 {\n            let total = total.eval(self.0);\n            if total == 0 {\n                formatter.write_str(\"0\")?;\n            } else if self.1 == total {\n                // Bump a 100% down to 50%. The only graph affected by this is\n                // `cargo tally --relative --transitive @alexcrichton` and while\n                // 50% is not an accurate datum, this hack makes that graph more\n                // readable by avoiding the y-axis getting extended all the way\n                // to 100% in the first day of crates.io's existence.\n                formatter.write_str(\"0.5\")?;\n            } else {\n                let fraction = self.1 as f32 / total as f32;\n                write_truncated(formatter, fraction)?;\n            }\n        } else {\n            write!(formatter, \"{}\", self.1)?;\n        }\n        formatter.write_str(\"},\\n\")?;\n        Ok(())\n    }\n}\n\nfn write_truncated(formatter: &mut fmt::Formatter, fraction: f32) -> fmt::Result {\n    let mut repr = fraction.to_string();\n    let nonzero_digit = |ch: char| ch >= '1' && ch <= '9';\n    if let Some(first_nonzero) = repr.find(nonzero_digit) {\n        repr.truncate(cmp::min(first_nonzero + 4, repr.len()));\n    }\n    if let Some(last_nonzero) = repr.rfind(nonzero_digit) {\n        repr.truncate(last_nonzero + 1);\n    }\n    formatter.write_str(&repr)\n}\n"
  },
  {
    "path": "src/stream.rs",
    "content": "macro_rules! stream {\n    ($k:ty => $v:ty; $r:ty) => {\n        stream![($k, $v); $r]\n    };\n    ($d:ty; $r:ty) => {\n        differential_dataflow::collection::Collection<\n            timely::dataflow::scopes::Child<\n                'a,\n                timely::worker::Worker<timely::communication::allocator::Process>,\n                crate::timestamp::DateTime,\n            >,\n            $d,\n            $r,\n        >\n    };\n}\n"
  },
  {
    "path": "src/timestamp.rs",
    "content": "use chrono::{NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};\nuse differential_dataflow::lattice::Lattice;\nuse std::cmp;\nuse std::fmt::{self, Debug, Display};\nuse timely::order::{PartialOrder, TotalOrder};\nuse timely::progress::timestamp::{PathSummary, Refines, Timestamp};\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]\n#[repr(transparent)]\npub struct DateTime(chrono::DateTime<Utc>);\n\n#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]\n#[repr(transparent)]\npub struct Duration(chrono::Duration);\n\nimpl DateTime {\n    pub fn new(date: NaiveDate, time: NaiveTime) -> Self {\n        DateTime(Utc.from_utc_datetime(&NaiveDateTime::new(date, time)))\n    }\n\n    pub fn now() -> Self {\n        DateTime(Utc::now())\n    }\n\n    pub fn seconds(&self) -> i64 {\n        self.0.timestamp()\n    }\n\n    pub fn millis(&self) -> i64 {\n        self.0.timestamp_millis()\n    }\n\n    pub fn subsec_nanos(&self) -> u32 {\n        self.0.timestamp_subsec_nanos()\n    }\n\n    pub fn from_timestamp(secs: i64, nanos: u32) -> Self {\n        DateTime(chrono::DateTime::from_timestamp(secs, nanos).unwrap())\n    }\n}\n\nimpl From<chrono::DateTime<Utc>> for DateTime {\n    fn from(date_time: chrono::DateTime<Utc>) -> Self {\n        DateTime(date_time)\n    }\n}\n\nimpl Timestamp for DateTime {\n    type Summary = Duration;\n\n    fn minimum() -> Self {\n        Self::from_timestamp(0, 0)\n    }\n}\n\nimpl Lattice for DateTime {\n    fn join(&self, other: &Self) -> Self {\n        cmp::max(*self, *other)\n    }\n\n    fn meet(&self, other: &Self) -> Self {\n        cmp::min(*self, *other)\n    }\n}\n\nimpl PartialOrder for DateTime {\n    fn less_than(&self, other: &Self) -> bool {\n        self < other\n    }\n\n    fn less_equal(&self, other: &Self) -> bool {\n        self <= other\n    }\n}\n\nimpl TotalOrder for DateTime {}\n\nimpl PathSummary<DateTime> for Duration {\n    fn results_in(&self, src: &DateTime) -> Option<DateTime> {\n        src.0.checked_add_signed(self.0).map(DateTime)\n    }\n\n    fn followed_by(&self, other: &Self) -> Option<Self> {\n        self.0.checked_add(&other.0).map(Duration)\n    }\n}\n\nimpl Refines<()> for DateTime {\n    fn to_inner(_other: ()) -> Self {\n        Self::minimum()\n    }\n\n    #[allow(clippy::unused_unit)]\n    fn to_outer(self) -> () {}\n\n    #[allow(clippy::unused_unit)]\n    fn summarize(_path: <Self as Timestamp>::Summary) -> () {}\n}\n\nimpl PartialOrder for Duration {\n    fn less_than(&self, other: &Self) -> bool {\n        self < other\n    }\n\n    fn less_equal(&self, other: &Self) -> bool {\n        self <= other\n    }\n}\n\nimpl Default for DateTime {\n    fn default() -> Self {\n        Self::minimum()\n    }\n}\n\nimpl Display for DateTime {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Display::fmt(&self.0, formatter)\n    }\n}\n\nimpl Debug for DateTime {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Debug::fmt(&self.0, formatter)\n    }\n}\n\nimpl Default for Duration {\n    fn default() -> Self {\n        Duration(chrono::Duration::nanoseconds(0))\n    }\n}\n\nimpl Debug for Duration {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Debug::fmt(&self.0, formatter)\n    }\n}\n"
  },
  {
    "path": "src/total.rs",
    "content": "use cargo_tally::timestamp::DateTime;\nuse cargo_tally::Release;\nuse std::collections::BTreeSet as Set;\n\npub(crate) struct Total {\n    times: Vec<DateTime>,\n}\n\nimpl Total {\n    pub(crate) fn index(releases: &[Release]) -> Self {\n        let mut crate_ids = Set::new();\n        let mut times = Vec::new();\n        for release in releases {\n            if crate_ids.insert(release.crate_id) {\n                times.push(release.created_at);\n            }\n        }\n        Total { times }\n    }\n\n    pub(crate) fn eval(&self, time: DateTime) -> u32 {\n        match self.times.binary_search(&time) {\n            Ok(i) => 1 + i as u32,\n            Err(i) => i as u32,\n        }\n    }\n}\n"
  },
  {
    "path": "src/trace.rs",
    "content": "pub(crate) const VERBOSE: bool = false;\n"
  },
  {
    "path": "src/user.rs",
    "content": "use ref_cast::RefCast;\nuse std::borrow::Borrow;\nuse std::cmp::Ordering;\nuse std::fmt::{self, Display};\n\npub(crate) fn valid(name: &str) -> bool {\n    name.chars().all(|ch| {\n        (ch >= '0' && ch <= '9')\n            || (ch >= 'A' && ch <= 'Z')\n            || (ch >= 'a' && ch <= 'z')\n            || ch == '-'\n    }) && !name.contains(\"--\")\n        && !name.starts_with('-')\n        && !name.ends_with('-')\n        && !name.is_empty()\n        && name.len() <= 39\n}\n\npub(crate) struct User(String);\n\nimpl User {\n    pub(crate) fn new(string: String) -> Self {\n        User(string)\n    }\n}\n\nimpl Ord for User {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        UserQuery::ref_cast(&self.0).cmp(UserQuery::ref_cast(&rhs.0))\n    }\n}\n\nimpl PartialOrd for User {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for User {}\n\nimpl PartialEq for User {\n    fn eq(&self, rhs: &Self) -> bool {\n        UserQuery::ref_cast(&self.0).eq(UserQuery::ref_cast(&rhs.0))\n    }\n}\n\nimpl Display for User {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Display::fmt(&self.0, formatter)\n    }\n}\n\n#[derive(RefCast)]\n#[repr(transparent)]\npub(crate) struct UserQuery(str);\n\nimpl UserQuery {\n    pub(crate) fn is_team(&self) -> bool {\n        self.0.contains('/')\n    }\n}\n\nimpl Borrow<UserQuery> for User {\n    fn borrow(&self) -> &UserQuery {\n        UserQuery::ref_cast(&self.0)\n    }\n}\n\nimpl Ord for UserQuery {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        self.0\n            .bytes()\n            .map(CaseAgnosticByte)\n            .cmp(rhs.0.bytes().map(CaseAgnosticByte))\n    }\n}\n\nimpl PartialOrd for UserQuery {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for UserQuery {}\n\nimpl PartialEq for UserQuery {\n    fn eq(&self, rhs: &Self) -> bool {\n        self.0\n            .bytes()\n            .map(CaseAgnosticByte)\n            .eq(rhs.0.bytes().map(CaseAgnosticByte))\n    }\n}\n\nimpl Display for UserQuery {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Display::fmt(&self.0, formatter)\n    }\n}\n\nstruct CaseAgnosticByte(u8);\n\nimpl Ord for CaseAgnosticByte {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase())\n    }\n}\n\nimpl PartialOrd for CaseAgnosticByte {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\n\nimpl Eq for CaseAgnosticByte {}\n\nimpl PartialEq for CaseAgnosticByte {\n    fn eq(&self, rhs: &Self) -> bool {\n        self.cmp(rhs) == Ordering::Equal\n    }\n}\n"
  },
  {
    "path": "src/version.rs",
    "content": "use crate::arena::Slice;\nuse semver::{Comparator, Op};\nuse std::cmp::Ordering;\nuse std::fmt::{self, Debug, Display};\nuse std::ops::{Deref, DerefMut};\nuse std::str::FromStr;\n\n#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]\npub struct Version(pub semver::Version);\n\nimpl Version {\n    pub const fn new(major: u64, minor: u64, patch: u64) -> Self {\n        Version(semver::Version::new(major, minor, patch))\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Hash)]\npub struct VersionReq {\n    pub comparators: Slice<Comparator>,\n}\n\nimpl VersionReq {\n    pub fn matches(&self, version: &Version) -> bool {\n        matches_req(self.comparators, version)\n    }\n}\n\nimpl Deref for Version {\n    type Target = semver::Version;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for Version {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl Display for Version {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        Display::fmt(&self.0, formatter)\n    }\n}\n\nimpl Debug for Version {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(formatter, \"Version({})\", self)\n    }\n}\n\nimpl Ord for VersionReq {\n    fn cmp(&self, other: &Self) -> Ordering {\n        let mut lhs = self.comparators.iter_ref();\n        let mut rhs = other.comparators.iter_ref();\n\n        loop {\n            let Some(x) = lhs.next() else {\n                return if rhs.next().is_none() {\n                    Ordering::Equal\n                } else {\n                    Ordering::Less\n                };\n            };\n\n            let Some(y) = rhs.next() else {\n                return Ordering::Greater;\n            };\n\n            match (x.op as usize, x.major, x.minor, x.patch, &x.pre).cmp(&(\n                y.op as usize,\n                y.major,\n                y.minor,\n                y.patch,\n                &y.pre,\n            )) {\n                Ordering::Equal => (),\n                non_eq => return non_eq,\n            }\n        }\n    }\n}\n\nimpl PartialOrd for VersionReq {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl From<semver::VersionReq> for VersionReq {\n    fn from(req: semver::VersionReq) -> Self {\n        let comparators = Slice::new(&req.comparators);\n        VersionReq { comparators }\n    }\n}\n\nimpl FromStr for VersionReq {\n    type Err = semver::Error;\n\n    fn from_str(string: &str) -> Result<Self, Self::Err> {\n        semver::VersionReq::from_str(string).map(VersionReq::from)\n    }\n}\n\nimpl Display for VersionReq {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        if self.comparators.is_empty() {\n            return formatter.write_str(\"*\");\n        }\n        for (i, comparator) in self.comparators.iter_ref().enumerate() {\n            if i > 0 {\n                formatter.write_str(\", \")?;\n            }\n            write!(formatter, \"{}\", comparator)?;\n        }\n        Ok(())\n    }\n}\n\nimpl Debug for VersionReq {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(formatter, \"VersionReq({})\", self)\n    }\n}\n\nfn matches_req(comparators: Slice<Comparator>, ver: &Version) -> bool {\n    for cmp in comparators.iter_ref() {\n        if !matches_impl(cmp, ver) {\n            return false;\n        }\n    }\n\n    if ver.pre.is_empty() {\n        return true;\n    }\n\n    // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it\n    // will only be allowed to satisfy req if at least one comparator with the\n    // same major.minor.patch also has a prerelease tag.\n    for cmp in comparators.iter_ref() {\n        if pre_is_compatible(cmp, ver) {\n            return true;\n        }\n    }\n\n    false\n}\n\nfn matches_impl(cmp: &Comparator, ver: &Version) -> bool {\n    match cmp.op {\n        Op::Exact | Op::Wildcard => matches_exact(cmp, ver),\n        Op::Greater => matches_greater(cmp, ver),\n        Op::GreaterEq => matches_exact(cmp, ver) || matches_greater(cmp, ver),\n        Op::Less => matches_less(cmp, ver),\n        Op::LessEq => matches_exact(cmp, ver) || matches_less(cmp, ver),\n        Op::Tilde => matches_tilde(cmp, ver),\n        Op::Caret => matches_caret(cmp, ver),\n        _ => unimplemented!(),\n    }\n}\n\nfn matches_exact(cmp: &Comparator, ver: &Version) -> bool {\n    if ver.major != cmp.major {\n        return false;\n    }\n\n    if let Some(minor) = cmp.minor {\n        if ver.minor != minor {\n            return false;\n        }\n    }\n\n    if let Some(patch) = cmp.patch {\n        if ver.patch != patch {\n            return false;\n        }\n    }\n\n    ver.pre == cmp.pre\n}\n\nfn matches_greater(cmp: &Comparator, ver: &Version) -> bool {\n    if ver.major != cmp.major {\n        return ver.major > cmp.major;\n    }\n\n    match cmp.minor {\n        None => return false,\n        Some(minor) => {\n            if ver.minor != minor {\n                return ver.minor > minor;\n            }\n        }\n    }\n\n    match cmp.patch {\n        None => return false,\n        Some(patch) => {\n            if ver.patch != patch {\n                return ver.patch > patch;\n            }\n        }\n    }\n\n    ver.pre > cmp.pre\n}\n\nfn matches_less(cmp: &Comparator, ver: &Version) -> bool {\n    if ver.major != cmp.major {\n        return ver.major < cmp.major;\n    }\n\n    match cmp.minor {\n        None => return false,\n        Some(minor) => {\n            if ver.minor != minor {\n                return ver.minor < minor;\n            }\n        }\n    }\n\n    match cmp.patch {\n        None => return false,\n        Some(patch) => {\n            if ver.patch != patch {\n                return ver.patch < patch;\n            }\n        }\n    }\n\n    ver.pre < cmp.pre\n}\n\nfn matches_tilde(cmp: &Comparator, ver: &Version) -> bool {\n    if ver.major != cmp.major {\n        return false;\n    }\n\n    if let Some(minor) = cmp.minor {\n        if ver.minor != minor {\n            return false;\n        }\n    }\n\n    if let Some(patch) = cmp.patch {\n        if ver.patch != patch {\n            return ver.patch > patch;\n        }\n    }\n\n    ver.pre >= cmp.pre\n}\n\nfn matches_caret(cmp: &Comparator, ver: &Version) -> bool {\n    if ver.major != cmp.major {\n        return false;\n    }\n\n    let Some(minor) = cmp.minor else {\n        return true;\n    };\n\n    let Some(patch) = cmp.patch else {\n        return if cmp.major > 0 {\n            ver.minor >= minor\n        } else {\n            ver.minor == minor\n        };\n    };\n\n    if cmp.major > 0 {\n        if ver.minor != minor {\n            return ver.minor > minor;\n        } else if ver.patch != patch {\n            return ver.patch > patch;\n        }\n    } else if minor > 0 {\n        if ver.minor != minor {\n            return false;\n        } else if ver.patch != patch {\n            return ver.patch > patch;\n        }\n    } else if ver.minor != minor || ver.patch != patch {\n        return false;\n    }\n\n    ver.pre >= cmp.pre\n}\n\nfn pre_is_compatible(cmp: &Comparator, ver: &Version) -> bool {\n    cmp.major == ver.major\n        && cmp.minor == Some(ver.minor)\n        && cmp.patch == Some(ver.patch)\n        && !cmp.pre.is_empty()\n}\n"
  }
]