├── .editorconfig ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── blank.md │ ├── bug_report.yml │ ├── config.yml │ └── feature.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── mdbook.yml │ ├── rust.yml │ └── typos.yml ├── .gitignore ├── .typos.toml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── banner.webp ├── book ├── .gitignore ├── book.toml ├── src │ ├── SUMMARY.md │ ├── config-table.md │ ├── configuration.md │ ├── credits.md │ ├── features.md │ ├── installation.md │ ├── keyfile-structure.md │ ├── nvrs.md │ └── package-entries.md └── theme │ ├── custom.css │ └── index.hbs ├── cliff.toml ├── justfile ├── man └── nvrs.1 ├── n_keyfile.toml ├── nvrs.tape ├── nvrs.toml ├── rustfmt.toml └── src ├── api ├── aur.rs ├── crates_io.rs ├── gitea.rs ├── github.rs ├── gitlab.rs ├── mod.rs └── regex.rs ├── cli ├── args.rs └── main.rs ├── config.rs ├── error.rs ├── keyfile.rs ├── lib.rs ├── tui └── main.rs └── verfiles.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.rs] 12 | indent_size = 4 13 | 14 | [*.md] 15 | indent_size = 4 16 | trim_trailing_whitespace = false 17 | 18 | [*.hbs] 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @adamperkowski 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: adamperkowski 2 | custom: "https://adamperkowski.dev/donate" 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/blank.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: blank issue 3 | about: create a blank issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: bug report 2 | description: report a bug to help us improve 3 | labels: ['bug'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | fill out the sections below to help us diagnose and fix the issue 9 | 10 | - type: textarea 11 | id: summary 12 | attributes: 13 | label: summary 14 | description: a clear description of what went wrong 15 | validations: 16 | required: true 17 | 18 | - type: textarea 19 | id: to_reproduce 20 | attributes: 21 | label: to reproduce 22 | description: list all steps required to reproduce the behavior 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | id: expected_behavior 28 | attributes: 29 | label: expected behavior 30 | description: what did you expect to happen 31 | validations: 32 | required: true 33 | 34 | - type: textarea 35 | id: actual_behavior 36 | attributes: 37 | label: actual behavior 38 | description: detailed description of what actually happened 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | id: additional 44 | attributes: 45 | label: additional context 46 | description: add any other context, screenshots, or logs to help us debug the issue 47 | validations: 48 | required: false 49 | 50 | - type: dropdown 51 | id: severity 52 | attributes: 53 | label: severity 54 | description: the level of importance 55 | options: 56 | - low 57 | - medium 58 | - high 59 | - critical 60 | validations: 61 | required: true 62 | 63 | - type: dropdown 64 | id: os 65 | attributes: 66 | label: OS 67 | description: the operating system where the bug occurred 68 | multiple: true 69 | options: 70 | - Linux 71 | - MacOS 72 | - Windows 73 | - Other 74 | validations: 75 | required: true 76 | 77 | - type: checkboxes 78 | id: checklist 79 | attributes: 80 | label: checklist 81 | description: check all that apply 82 | options: 83 | - label: 'i have checked the documentation' 84 | required: true 85 | - label: 'i have read the Code of Conduct' 86 | required: true 87 | - label: 'this is NOT a secutity vulnerability (refer to SECUTITY.md)' 88 | required: true 89 | - label: 'there are no known workarounds' 90 | required: false 91 | - label: 'there are no already existing issues or pull requests for this bug' 92 | required: true 93 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: nvrs documentation 4 | url: https://nvrs.adamperkowski.dev 5 | about: check out the docs 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: feature request 2 | description: request a new feature for nvrs 3 | labels: ['enhancement'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | fill out the sections below to help us understand your request 9 | 10 | - type: textarea 11 | id: summary 12 | attributes: 13 | label: summary 14 | description: a clear description of the feature 15 | validations: 16 | required: true 17 | 18 | - type: textarea 19 | id: problem 20 | attributes: 21 | label: the problem 22 | description: is this request related to a problem? please describe 23 | validations: 24 | required: false 25 | 26 | - type: textarea 27 | id: usecase 28 | attributes: 29 | label: use case 30 | description: describe the use case for this feature 31 | validations: 32 | required: false 33 | 34 | - type: textarea 35 | id: additional 36 | attributes: 37 | label: additional context 38 | description: add any other context or information avout this feature 39 | validations: 40 | required: false 41 | 42 | - type: dropdown 43 | id: severity 44 | attributes: 45 | label: importance 46 | description: the level of importance 47 | options: 48 | - low 49 | - medium 50 | - high 51 | - critical 52 | validations: 53 | required: true 54 | 55 | - type: checkboxes 56 | id: checklist 57 | attributes: 58 | label: checklist 59 | description: check all that apply 60 | options: 61 | - label: 'i have checked the documentation' 62 | required: true 63 | - label: 'i have read the Code of Conduct' 64 | required: true 65 | - label: 'there are no known workarounds' 66 | required: false 67 | - label: 'there are no already existing issues or pull requests for this feature' 68 | required: true 69 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # [TITLE] 5 | 6 | ## Description 7 | 8 | 9 | ## Context 10 | 11 | 12 | 14 | 15 | ## Testing 16 | 17 | 18 | ## Screenshots / Logs (if applicable) 19 | 20 | ## Checklist 21 | 22 | - [ ] My code adheres to the coding and style guidelines of the project. 23 | - [ ] I have performed a self-review of my own code. 24 | - [ ] I have commented my code, particularly in hard-to-understand areas. 25 | - [ ] I made corresponding changes to the documentation. 26 | - [ ] I formatted the code with [rustfmt](https://github.com/rust-lang/rustfmt). 27 | - [ ] I checked the lints with [clippy](https://github.com/rust-lang/rust-clippy). 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | **Reporting a Vulnerability** 4 | 5 | If you have any reason to believe there are security vulnerabilities in this GitHub repository, report to [adas1per@protonmail.com](mailto:adas1per@protonmail.com) or through a [report form](https://github.com/adamperkowski/highlightos/security/advisories/new). 6 |
We will respond as soon as we are able to (usually about **1-3 days**). If the report is confirmed to be true, we will do our best to fix the issue as soon as possible. 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "cargo" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | assignees: 10 | - "adamperkowski" 11 | labels: 12 | - "deps" 13 | commit-message: 14 | prefix: "chore(deps.cargo)" 15 | -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | name: mdBook 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '*' 9 | 10 | concurrency: 11 | group: "pages" 12 | cancel-in-progress: false 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: write 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Setup mdBook 24 | uses: peaceiris/actions-mdbook@v2 25 | with: 26 | mdbook-version: 'latest' 27 | 28 | - run: mdbook build book 29 | 30 | - name: Set output directory 31 | run: | 32 | OUTDIR=$(basename ${{ github.ref }}) 33 | echo "OUTDIR=$OUTDIR" >> $GITHUB_ENV 34 | 35 | - name: Deploy stable 36 | uses: peaceiris/actions-gh-pages@v4 37 | if: startswith(github.ref, 'refs/tags/') 38 | with: 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | publish_dir: ./book/book 41 | 42 | - name: Deploy 43 | uses: peaceiris/actions-gh-pages@v4 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | publish_dir: ./book/book 47 | destination_dir: ./${{ env.OUTDIR }} 48 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | on: 3 | push: 4 | branches: ["main"] 5 | paths: 6 | - '**/*.rs' 7 | - 'src/**/*' 8 | - 'Cargo.toml' 9 | - 'Cargo.lock' 10 | pull_request: 11 | paths: 12 | - '**/*.rs' 13 | - 'src/**/*' 14 | - 'Cargo.toml' 15 | - 'Cargo.lock' 16 | workflow_dispatch: 17 | 18 | env: 19 | CARGO_TERM_COLOR: always 20 | 21 | jobs: 22 | test: 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: install Rust 29 | uses: dtolnay/rust-toolchain@stable 30 | 31 | - name: Install Just 32 | uses: extractions/setup-just@v2 33 | 34 | - run: just test 35 | -------------------------------------------------------------------------------- /.github/workflows/typos.yml: -------------------------------------------------------------------------------- 1 | name: Check for typos 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | check-typos: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Run spellcheck 17 | uses: crate-ci/typos@v1.25.0 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | newver.json 3 | oldver.json 4 | keyfile.toml 5 | *_old 6 | *.old 7 | *.gif 8 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = [ 3 | "CHANGELOG.md", 4 | "book/theme/*" 5 | ] 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to nvrs will be documented in this file. 4 | 5 | ## [0.1.8-pre1] - 2025-03-18 6 | 7 | ### 🛠️ Refactoring 8 | 9 | - (*config*) support XDG standard ([22c6424](https://github.com/adamperkowski/nvrs/commit/22c64243c571914ece818b86523e337e416ff669)) 10 | - (*justfile*) release structure ([4bab7d5](https://github.com/adamperkowski/nvrs/commit/4bab7d502453d45461ad051d35d3635935695465)) 11 | - [**breaking**] rust 2024 ([79d62d2](https://github.com/adamperkowski/nvrs/commit/79d62d2198e447d4706ac50d76c26ad3553a4c9a)) 12 | 13 | ### ⚡ Performance 14 | 15 | - some optimizations ([#44](https://github.com/adamperkowski/nvrs/issues/44)) ([6d423d5](https://github.com/adamperkowski/nvrs/commit/6d423d5eeb52806978d971891c4c955b2140f85e)) 16 | 17 | ## [0.1.7] - 2025-02-05 18 | 19 | ### 🚀 Features 20 | 21 | - (*verfiles, keyfile*) expand `~` to `$HOME` in paths ([fb0024f](https://github.com/adamperkowski/nvrs/commit/fb0024f8b01eff51ff5b5a90035fdcb1e62340da)) 22 | 23 | ### 📚 Documentation 24 | 25 | - add a GitHub repo link to `nvrs.md` ([ae0fb41](https://github.com/adamperkowski/nvrs/commit/ae0fb4106dc35694fb4ce7210a8ae3a8f5bc881c)) 26 | 27 | ### ⚙️ Miscellaneous Tasks 28 | 29 | - add a justfile ([3e506aa](https://github.com/adamperkowski/nvrs/commit/3e506aac53be407aaa3ab9c2967687b1573b9891)) 30 | 31 | ## [0.1.7-pre1] - 2025-01-06 32 | 33 | ### 📚 Documentation 34 | 35 | - (*PR*) remove changelog requirement ([b61129d](https://github.com/adamperkowski/nvrs/commit/b61129d53cff5b608b01fbf83650d917765bdce9)) 36 | - (*issue_template*) add templates ([e603590](https://github.com/adamperkowski/nvrs/commit/e603590cf97aeabef9933b762e239a54ab72f40e)) 37 | - (*mdbook*) we're up ([#29](https://github.com/adamperkowski/nvrs/issues/29)) ([154792d](https://github.com/adamperkowski/nvrs/commit/154792d75e76beb636af1807ec1e8114a8c4af64)) 38 | 39 | ### ⚙️ Miscellaneous Tasks 40 | 41 | - improve secret security by using `pub(crate)` ([9baee92](https://github.com/adamperkowski/nvrs/commit/9baee9208c72c9544f68140b421ca3a28f0ec510)) 42 | 43 | ## [0.1.6] - 2025-01-02 44 | 45 | ### 🚀 Features 46 | 47 | - `--list-sources` command ([442c06f](https://github.com/adamperkowski/nvrs/commit/442c06f0e56f4adcc0c2ad44d042997cb088a930)) 48 | - (*sources*) add regex ([#13](https://github.com/adamperkowski/nvrs/issues/13)) ([fa12ce9](https://github.com/adamperkowski/nvrs/commit/fa12ce9691adbdcf51990eb8416aedf4fdc7d36b)) 49 | - (*sources*) add crates.io ([78294ff](https://github.com/adamperkowski/nvrs/commit/78294ff44f26ce79df0c7c7c2511517627fc4a31)) 50 | - (*sources*) add gitea ([4b015d5](https://github.com/adamperkowski/nvrs/commit/4b015d55f4064b8a735bd87a2e1c879cff5bd86c)) 51 | 52 | ### 🐛 Bug Fixes 53 | 54 | - incorrect `--compare` colors ([ef1f78f](https://github.com/adamperkowski/nvrs/commit/ef1f78fed76f883986734b7e3220b2f56508a5f0)) 55 | - (*io*) not shutting down file streams after writing ([a8a42fd](https://github.com/adamperkowski/nvrs/commit/a8a42fdf03bfde7aeee563fbd6f9d7af832bc70e)) 56 | 57 | ### 🛠️ Refactoring 58 | 59 | - (*verfiles, config*) saving & loading improvements ([81d7efd](https://github.com/adamperkowski/nvrs/commit/81d7efd24b9b425f59bec1cdbb588bc25ed433cb)) 60 | - split features & binaries ([4acd98e](https://github.com/adamperkowski/nvrs/commit/4acd98e5d2c5df0845c6bd512cb40f705da9361e)) 61 | 62 | ### 📚 Documentation 63 | 64 | - more details & improvements ([9f02405](https://github.com/adamperkowski/nvrs/commit/9f02405339c3520340899313365f0de2fb3d65c5)) 65 | - (*README*) update cargo install instructions ([88f0fdc](https://github.com/adamperkowski/nvrs/commit/88f0fdce435c50df44c3ae2cfd5d1087df4376fc)) 66 | - improve the wording ([c47df3d](https://github.com/adamperkowski/nvrs/commit/c47df3dbe342bacc79ba2099f17365f07555cdd3)) 67 | 68 | ### ⚡ Performance 69 | 70 | - reduce cloning & improve overall performance ([0777ef2](https://github.com/adamperkowski/nvrs/commit/0777ef24b54f056d7bd66c8b932b478cbbc3eb66)) 71 | 72 | ### ⚙️ Miscellaneous Tasks 73 | 74 | - (*repo*) fix dependabot ([432b10f](https://github.com/adamperkowski/nvrs/commit/432b10f32199ecd7a33c2d9643a5e1f512db862c)) 75 | - (*ci*) remove `cliff.yml` ([f57c3ab](https://github.com/adamperkowski/nvrs/commit/f57c3ab39fc9a3db68cbd26fc7ac6a93b1c89c20)) 76 | - update copyright year ([98f8f6d](https://github.com/adamperkowski/nvrs/commit/98f8f6dee86e2b2c1a90c5b83bb54d7985cf466b)) 77 | 78 | ### Other (unconventional) 79 | 80 | - _ ([42da383](https://github.com/adamperkowski/nvrs/commit/42da3838cc80f5929fb592cfa8468f166238c32f)) 81 | - add the grind compliant badge ([3372b3e](https://github.com/adamperkowski/nvrs/commit/3372b3e7505d4854b10a63851743d54acffe1f7b)) 82 | 83 | ## [0.1.5] - 2024-11-29 84 | 85 | ### 🚀 Features 86 | 87 | - `use_max_tag` functionality ([8431412](https://github.com/adamperkowski/nvrs/commit/843141248520b7a784cae15c0571cd23e68d277e)) 88 | 89 | ### 🐛 Bug Fixes 90 | 91 | - (*ui*) `sync` errors displayed twice when no `--no-fail` ([8d7e341](https://github.com/adamperkowski/nvrs/commit/8d7e3413e258ac1b1a38256de10f02d8f078d68d)) 92 | - `Package` default() & new() features ([779c1ef](https://github.com/adamperkowski/nvrs/commit/779c1ef7e21c4ed6c31a524797a3f94c8678a3d5)) 93 | 94 | ### 🛠️ Refactoring 95 | 96 | - (*features*) remove `http` ([712bcea](https://github.com/adamperkowski/nvrs/commit/712bceae2626838af664df10dd967cb4a2819ab8)) 97 | 98 | ### 📚 Documentation 99 | 100 | - (*README*) add installation & usage instructions ([cd0bd72](https://github.com/adamperkowski/nvrs/commit/cd0bd7269f35ccb559f81abfda62c69ae06bce79)) 101 | 102 | ### 🧪 Testing 103 | 104 | - `Package` default(), new() & tests ([ca96da8](https://github.com/adamperkowski/nvrs/commit/ca96da8381da62cea1b01fd1f1d0363b7e5d1f9b)) 105 | - add benchmarking ([#5](https://github.com/adamperkowski/nvrs/issues/5)) ([3e1ef8d](https://github.com/adamperkowski/nvrs/commit/3e1ef8dbc6030073523d332652b3200016591071)) 106 | 107 | ## [0.1.4] - 2024-11-25 108 | 109 | ### 🚀 Features 110 | 111 | - (*take*) `ALL` functionality ([0ee83eb](https://github.com/adamperkowski/nvrs/commit/0ee83eb785f939780c8e07920c1f98a8a258d158)) 112 | 113 | ### 🐛 Bug Fixes 114 | 115 | - (*verfile*) allow missing gitref & url ([b93216d](https://github.com/adamperkowski/nvrs/commit/b93216d5146a672897e11938668e05cfa859cfac)) 116 | - `--nuke` not working ([15b75d9](https://github.com/adamperkowski/nvrs/commit/15b75d99667a4c52d0d9b093704aa02ca4d35e3e)) 117 | 118 | ### 🛠️ Refactoring 119 | 120 | - (*codebase*) [**breaking**] move internal logic to `lib` ([#4](https://github.com/adamperkowski/nvrs/issues/4)) ([c0021f0](https://github.com/adamperkowski/nvrs/commit/c0021f0a4e02791802fba9ba6bca5486f825ee4e)) 121 | 122 | ### 📚 Documentation 123 | 124 | - (*git-cliff*) add `UI/UX` ([42727ad](https://github.com/adamperkowski/nvrs/commit/42727ad6bd020ecee06e93017e7e5b68851c01d3)) 125 | - (*config*) fix the package name (alpm -> mkinitcpio) ([1327516](https://github.com/adamperkowski/nvrs/commit/132751692941f5e1e2cce188d545f3ee421dad46)) 126 | - better banner ([a4718b6](https://github.com/adamperkowski/nvrs/commit/a4718b60505d26c2e262b70d77160b475b8f2348)) 127 | - (*dependabot*) change cargo commit message ([90d50ab](https://github.com/adamperkowski/nvrs/commit/90d50ab0fd6cd4964408796e2f75affeb539923b)) 128 | - 🚦 ([f2e22b6](https://github.com/adamperkowski/nvrs/commit/f2e22b6c8daece310080a8e32d183e0f6ef3e3f0)) 129 | 130 | ### 🧩 UI/UX 131 | 132 | - (*output*) print out `NONE` take information ([71cb36f](https://github.com/adamperkowski/nvrs/commit/71cb36f913035d484bf26d8a2c3430132ea176ba)) 133 | 134 | ## [0.1.3] - 2024-11-18 135 | 136 | ### 🐛 Bug Fixes 137 | 138 | - not updating newver refs & urls ([2d3c48e](https://github.com/adamperkowski/nvrs/commit/2d3c48e097beb569dae2d610f35aaec03614e835)) 139 | - saving empty strings ([4b7a48a](https://github.com/adamperkowski/nvrs/commit/4b7a48a49ad39e49e2d98f4b87c2d7eb387c843d)) 140 | - messed up packages order ([8e5d63a](https://github.com/adamperkowski/nvrs/commit/8e5d63ad97b66fa5783d579241ba82c7499a47d2)) 141 | 142 | ### 📚 Documentation 143 | 144 | - (*config*) add a keyfile ([6f4ec82](https://github.com/adamperkowski/nvrs/commit/6f4ec82d58e099feca69b757c864da7a932a84fa)) 145 | - (*manpage*) add `--no-fail` ([86b7b6e](https://github.com/adamperkowski/nvrs/commit/86b7b6ef02dc2be981ffd8b5597c56b0dd70f27d)) 146 | - CONTRIBUTING.md, CODE_OF_CONDUCT.md, README badges, PULL_REQUEST_TEMPLATE.md ([ece2f01](https://github.com/adamperkowski/nvrs/commit/ece2f01ac8934c0bf4ae1eca4d895896ed1ac336)) 147 | - (*git-cliff*) disable github usernames ([889d365](https://github.com/adamperkowski/nvrs/commit/889d365dbd53f861ff4aa85633599a0996b09326)) 148 | 149 | ### ⚡ Performance 150 | 151 | - I. AM. SPEED. ([6933f8c](https://github.com/adamperkowski/nvrs/commit/6933f8ccafa0ae4f195e65921541e5eeb12b05fb)) 152 | 153 | ### ⚙️ Miscellaneous Tasks 154 | 155 | - fix invalid `is_empty_string` type ([1e8ae8f](https://github.com/adamperkowski/nvrs/commit/1e8ae8f44c24a1a70c7424c979ab4b654000f29c)) 156 | 157 | ## [0.1.2] - 2024-11-17 158 | 159 | ### 🚀 Features 160 | 161 | - (*sources*) multiple sources + AUR ([8322ada](https://github.com/adamperkowski/nvrs/commit/8322adaac003dd9210bd291399b275eb5daaf673)) 162 | - `--no-fail` ([4db55bc](https://github.com/adamperkowski/nvrs/commit/4db55bcd2ff55c7c137f511ce40999b6afe2b3f7)) 163 | - gitlab support ([4c46d82](https://github.com/adamperkowski/nvrs/commit/4c46d828bd55196a1ea094b5a2f9d037948b87e1)) 164 | - [**breaking**] keyfiles ([8ae2c27](https://github.com/adamperkowski/nvrs/commit/8ae2c27b71cb3fabd66623a13b9a8241c56deaad)) 165 | 166 | ### 🐛 Bug Fixes 167 | 168 | - (*aur*) quotes ([b1b3fcf](https://github.com/adamperkowski/nvrs/commit/b1b3fcf64c7591dc87ba201ecf54a4029fbd1960)) 169 | - (*aur*) quotes again ([9c2fedf](https://github.com/adamperkowski/nvrs/commit/9c2fedf1d7d4bbebe5a1ca9d8bfd204daee4283f)) 170 | 171 | ### 📚 Documentation 172 | 173 | - (*readme*) add `sources` ([0823f46](https://github.com/adamperkowski/nvrs/commit/0823f46aea5e19f31605360849bfeec2389c51af)) 174 | 175 | ### ⚙️ Miscellaneous Tasks 176 | 177 | - more `cargo` metadata ([6b6ebd6](https://github.com/adamperkowski/nvrs/commit/6b6ebd680f49d22c053360f7b542ba074e3eb2b1)) 178 | - (*main.rs*) collapse the `latest` `else if` statement ([3cdb71d](https://github.com/adamperkowski/nvrs/commit/3cdb71dc8e1759eb6a3309d5fe45dfe95663fc02)) 179 | - (*gitignore*) add `keyfile.toml` ([602b91f](https://github.com/adamperkowski/nvrs/commit/602b91fba795ec8916bbdb4131d4a89975b157bf)) 180 | 181 | ## [0.1.1] - 2024-11-17 182 | 183 | ### 🚀 Features 184 | 185 | - `--nuke` functionality + some minor fixes ([6949ec0](https://github.com/adamperkowski/nvrs/commit/6949ec0c36c3634dafd0123b5ee7cbd4c092e0c9)) 186 | - add `--version` & about ([50f2bc2](https://github.com/adamperkowski/nvrs/commit/50f2bc246aa32b0f50fb3aa55580c56559c5ee64)) 187 | 188 | ### 🐛 Bug Fixes 189 | 190 | - (*ui*) wrong --cmp output characters ([3cad4c1](https://github.com/adamperkowski/nvrs/commit/3cad4c1dd94f54c176d894e32c4f7ef384c6d8dd)) 191 | - (*config*) make `prefix` optional ([7b942cc](https://github.com/adamperkowski/nvrs/commit/7b942cc6b9f7c5ac551837e7f53425df34ccb3a9)) 192 | 193 | ### 📚 Documentation 194 | 195 | - add a banner & move `speed` to `features` ([752fc15](https://github.com/adamperkowski/nvrs/commit/752fc158b118de603a9f2a9f31a0c320fb3cf78a)) 196 | - add a manpage ([073c98f](https://github.com/adamperkowski/nvrs/commit/073c98ff097283fae09742c77bb98358d706bb22)) 197 | - some `git-cliff` improvements ([270c0e6](https://github.com/adamperkowski/nvrs/commit/270c0e6b6e729a349b61a512def02433d3675cc9)) 198 | - more `git-cliff` improvements ([83ae70f](https://github.com/adamperkowski/nvrs/commit/83ae70fd0e2820158a56a86a05aa6f619ae6b141)) 199 | 200 | ### ⚡ Performance 201 | 202 | - drastically decrease bin size & increase performance ([460f9d9](https://github.com/adamperkowski/nvrs/commit/460f9d9bbe6928d34948ecb3eec7fd0c6c4b7ba4)) 203 | 204 | ### Other (unconventional) 205 | 206 | - change the `--take` character ([0aace9d](https://github.com/adamperkowski/nvrs/commit/0aace9de0f2c3f26eda4de9491a3454929398102)) 207 | 208 | ## [0.1.0] - 2024-11-16 209 | 210 | ### 🐛 Bug Fixes 211 | 212 | - (*hot*) a typo in `custom_error` ([4844515](https://github.com/adamperkowski/nvrs/commit/48445157be6b3ae9ca97d6c79f25b20529e30fd7)) 213 | 214 | ### 🛠️ Refactoring 215 | 216 | - (*custom_error*) improve newline control ([#1](https://github.com/adamperkowski/nvrs/issues/1)) ([05faaca](https://github.com/adamperkowski/nvrs/commit/05faaca79dd1306a818864ab80ae028a0217dd1e)) 217 | 218 | ### ⚙️ Miscellaneous Tasks 219 | 220 | - GitHub stuff ([eda40d8](https://github.com/adamperkowski/nvrs/commit/eda40d8d68c4c13d24ad2b9b0acd217c02ee889e)) 221 | - run git-cliff on schedule ([c18f152](https://github.com/adamperkowski/nvrs/commit/c18f15256d041c17f1a47e6310c08ce23fc286f2)) 222 | - exclude `CHANGELOG.md` from `typos` ([bbdd835](https://github.com/adamperkowski/nvrs/commit/bbdd83543aa49be2ca690e767d42d5572e3ee2a8)) 223 | 224 | ### Other (unconventional) 225 | 226 | - init ([4ca8ba6](https://github.com/adamperkowski/nvrs/commit/4ca8ba6f390d668e8d13caa0214f97c09115d4c3)) 227 | - set up workflows ([86933da](https://github.com/adamperkowski/nvrs/commit/86933da3817c26fa3caa6a84bb3ecf4c4d2cae2a)) 228 | - rebranding ([b927a53](https://github.com/adamperkowski/nvrs/commit/b927a536fddbde155979ef03ef0b800906ef777b)) 229 | - cli args ([97cca62](https://github.com/adamperkowski/nvrs/commit/97cca6211308b3eef82f16e8289527e7490f10a4)) 230 | - config ([b03dc12](https://github.com/adamperkowski/nvrs/commit/b03dc12e3686f0ef5e21f43731189a771d08d475)) 231 | - github api ([9c92e24](https://github.com/adamperkowski/nvrs/commit/9c92e24d3a2a82eaaf84f3b37ce342a8b88181cd)) 232 | - better cli ([683ffd7](https://github.com/adamperkowski/nvrs/commit/683ffd77f6fc03067b9929ee4c50f3c8600e75ff)) 233 | - custom configs ([51b78ba](https://github.com/adamperkowski/nvrs/commit/51b78baf83eb9f1fb2190974a2668263d1ce2e6c)) 234 | - verfiles + updating ([345f8fd](https://github.com/adamperkowski/nvrs/commit/345f8fda053074c150e7595e611b8d44dd603786)) 235 | - new entry saving ([62075ec](https://github.com/adamperkowski/nvrs/commit/62075ecdb5d4666b6b3fec6e02c42913f544c75c)) 236 | - compare & take ([18d538f](https://github.com/adamperkowski/nvrs/commit/18d538f738be4060fb65388cb822f09c8e00aebf)) 237 | 238 | generated by [git-cliff](https://github.com/orhun/git-cliff) :) 239 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # nvrs Code of Conduct 2 | **Contact:** [adas1per@protonmail.com](mailto:adas1per@protonmail.com) 3 | 4 | ## Conduct 5 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless of anything. 6 | * Please be kind, friendly and active - don't abandon your work without communicating. 7 | * Respect different perspectives and opinions. Be tolerant of opposing views, disagreements should be addressed with respect and courtesy. 8 | * Any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. 9 | 10 | ## Moderation 11 | These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact me (email above) or other [CODE OWNERS](https://github.com/adamperkowski/highlightos/blob/main/.github/CODEOWNERS). 12 | 13 | ## Attribution 14 | This Code of Conduct is inspired by [The Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering contributing to [nvrs](https://github.com/adamperkowski/nvrs) ❤️ 4 | 5 | If, while viewing the code, you find any parts unclear or unexplained, please [open an issue](https://github.com/adamperkowski/nvrs/issues/new/choose) with a documentation request. 6 | 7 | Note that we have a [Code of Conduct](./CODE_OF_CONDUCT.md). Please follow it in all your interactions with the project. 8 | 9 | ## Workflow 10 | 11 | 1. Fork this repository and create your branch from `main`. 12 | 13 | 2. Clone your forked repository. 14 | 15 | ```sh 16 | git clone https://github.com/adamperkowski/nvrs && cd nvrs 17 | # OR 18 | git clone git@github.com:adamperkowski/nvrs && cd nvrs 19 | ``` 20 | 21 | 3. Make sure that you have [Rust](https://rust-lang.org) installed and build the project. 22 | 23 | ```sh 24 | cargo build 25 | ``` 26 | 27 | 4. Start committing your changes. Follow the [conventional commit specification](https://conventionalcommits.org). 28 | 29 | 5. Make sure [rustfmt](https://github.com/rust-lang/rustfmt) and [clippy](https://github.com/rust-lang/rust-clippy) don't complain about your changes. 30 | 31 | 6. If needed, update the changelog. Make sure that you have [git-cliff](https://github.com/orhun/git-cliff) installed. 32 | 33 | ```sh 34 | git-cliff > CHANGELOG.md 35 | git add CHANGELOG.md 36 | git commit -m "changelog for $(git rev-parse --short HEAD)" 37 | ``` 38 | 39 | ## Submitting Changes 40 | 41 | 1. Ensure that you updated the documentation and filled the [Pull Request template](./.github/PULL_REQUEST_TEMPLATE.md) according to the changes you made. 42 | 43 | 2. Push the changes and [open a Pull Request](https://github.com/adamperkowski/nvrs/pull/new). 44 | 45 | 3. Wait for review from the project's [CODE OWNER](./.github/CODEOWNERS). Update the Pull Request accordingly. 46 | 47 | 4. Once the Pull Request is approved, it will be merged into the main branch. 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anes" 31 | version = "0.1.6" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 34 | 35 | [[package]] 36 | name = "anstream" 37 | version = "0.6.18" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 40 | dependencies = [ 41 | "anstyle", 42 | "anstyle-parse", 43 | "anstyle-query", 44 | "anstyle-wincon", 45 | "colorchoice", 46 | "is_terminal_polyfill", 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle" 52 | version = "1.0.10" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 55 | 56 | [[package]] 57 | name = "anstyle-parse" 58 | version = "0.2.6" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 61 | dependencies = [ 62 | "utf8parse", 63 | ] 64 | 65 | [[package]] 66 | name = "anstyle-query" 67 | version = "1.1.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 70 | dependencies = [ 71 | "windows-sys 0.59.0", 72 | ] 73 | 74 | [[package]] 75 | name = "anstyle-wincon" 76 | version = "3.0.7" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 79 | dependencies = [ 80 | "anstyle", 81 | "once_cell", 82 | "windows-sys 0.59.0", 83 | ] 84 | 85 | [[package]] 86 | name = "async-stream" 87 | version = "0.3.6" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" 90 | dependencies = [ 91 | "async-stream-impl", 92 | "futures-core", 93 | "pin-project-lite", 94 | ] 95 | 96 | [[package]] 97 | name = "async-stream-impl" 98 | version = "0.3.6" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" 101 | dependencies = [ 102 | "proc-macro2", 103 | "quote", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "autocfg" 109 | version = "1.4.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 112 | 113 | [[package]] 114 | name = "backtrace" 115 | version = "0.3.74" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 118 | dependencies = [ 119 | "addr2line", 120 | "cfg-if", 121 | "libc", 122 | "miniz_oxide", 123 | "object", 124 | "rustc-demangle", 125 | "windows-targets 0.52.6", 126 | ] 127 | 128 | [[package]] 129 | name = "base64" 130 | version = "0.22.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 133 | 134 | [[package]] 135 | name = "bitflags" 136 | version = "2.9.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 139 | 140 | [[package]] 141 | name = "bumpalo" 142 | version = "3.17.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 145 | 146 | [[package]] 147 | name = "bytes" 148 | version = "1.10.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 151 | 152 | [[package]] 153 | name = "cast" 154 | version = "0.3.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 157 | 158 | [[package]] 159 | name = "cc" 160 | version = "1.2.16" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" 163 | dependencies = [ 164 | "shlex", 165 | ] 166 | 167 | [[package]] 168 | name = "cfg-if" 169 | version = "1.0.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 172 | 173 | [[package]] 174 | name = "ciborium" 175 | version = "0.2.2" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 178 | dependencies = [ 179 | "ciborium-io", 180 | "ciborium-ll", 181 | "serde", 182 | ] 183 | 184 | [[package]] 185 | name = "ciborium-io" 186 | version = "0.2.2" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 189 | 190 | [[package]] 191 | name = "ciborium-ll" 192 | version = "0.2.2" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 195 | dependencies = [ 196 | "ciborium-io", 197 | "half", 198 | ] 199 | 200 | [[package]] 201 | name = "clap" 202 | version = "4.5.38" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" 205 | dependencies = [ 206 | "clap_builder", 207 | "clap_derive", 208 | ] 209 | 210 | [[package]] 211 | name = "clap_builder" 212 | version = "4.5.38" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" 215 | dependencies = [ 216 | "anstream", 217 | "anstyle", 218 | "clap_lex", 219 | ] 220 | 221 | [[package]] 222 | name = "clap_derive" 223 | version = "4.5.32" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 226 | dependencies = [ 227 | "heck", 228 | "proc-macro2", 229 | "quote", 230 | "syn", 231 | ] 232 | 233 | [[package]] 234 | name = "clap_lex" 235 | version = "0.7.4" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 238 | 239 | [[package]] 240 | name = "colorchoice" 241 | version = "1.0.3" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 244 | 245 | [[package]] 246 | name = "colored" 247 | version = "3.0.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" 250 | dependencies = [ 251 | "windows-sys 0.59.0", 252 | ] 253 | 254 | [[package]] 255 | name = "core-foundation" 256 | version = "0.9.4" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 259 | dependencies = [ 260 | "core-foundation-sys", 261 | "libc", 262 | ] 263 | 264 | [[package]] 265 | name = "core-foundation-sys" 266 | version = "0.8.7" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 269 | 270 | [[package]] 271 | name = "criterion" 272 | version = "0.6.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" 275 | dependencies = [ 276 | "anes", 277 | "cast", 278 | "ciborium", 279 | "clap", 280 | "criterion-plot", 281 | "itertools 0.13.0", 282 | "num-traits", 283 | "oorandom", 284 | "plotters", 285 | "rayon", 286 | "regex", 287 | "serde", 288 | "serde_json", 289 | "tinytemplate", 290 | "walkdir", 291 | ] 292 | 293 | [[package]] 294 | name = "criterion-plot" 295 | version = "0.5.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 298 | dependencies = [ 299 | "cast", 300 | "itertools 0.10.5", 301 | ] 302 | 303 | [[package]] 304 | name = "crossbeam-deque" 305 | version = "0.8.6" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 308 | dependencies = [ 309 | "crossbeam-epoch", 310 | "crossbeam-utils", 311 | ] 312 | 313 | [[package]] 314 | name = "crossbeam-epoch" 315 | version = "0.9.18" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 318 | dependencies = [ 319 | "crossbeam-utils", 320 | ] 321 | 322 | [[package]] 323 | name = "crossbeam-utils" 324 | version = "0.8.21" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 327 | 328 | [[package]] 329 | name = "crunchy" 330 | version = "0.2.3" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" 333 | 334 | [[package]] 335 | name = "displaydoc" 336 | version = "0.2.5" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 339 | dependencies = [ 340 | "proc-macro2", 341 | "quote", 342 | "syn", 343 | ] 344 | 345 | [[package]] 346 | name = "either" 347 | version = "1.15.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 350 | 351 | [[package]] 352 | name = "equivalent" 353 | version = "1.0.2" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 356 | 357 | [[package]] 358 | name = "errno" 359 | version = "0.3.10" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 362 | dependencies = [ 363 | "libc", 364 | "windows-sys 0.59.0", 365 | ] 366 | 367 | [[package]] 368 | name = "fastrand" 369 | version = "2.3.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 372 | 373 | [[package]] 374 | name = "fnv" 375 | version = "1.0.7" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 378 | 379 | [[package]] 380 | name = "foreign-types" 381 | version = "0.3.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 384 | dependencies = [ 385 | "foreign-types-shared", 386 | ] 387 | 388 | [[package]] 389 | name = "foreign-types-shared" 390 | version = "0.1.1" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 393 | 394 | [[package]] 395 | name = "form_urlencoded" 396 | version = "1.2.1" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 399 | dependencies = [ 400 | "percent-encoding", 401 | ] 402 | 403 | [[package]] 404 | name = "futures" 405 | version = "0.3.31" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 408 | dependencies = [ 409 | "futures-channel", 410 | "futures-core", 411 | "futures-io", 412 | "futures-sink", 413 | "futures-task", 414 | "futures-util", 415 | ] 416 | 417 | [[package]] 418 | name = "futures-channel" 419 | version = "0.3.31" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 422 | dependencies = [ 423 | "futures-core", 424 | "futures-sink", 425 | ] 426 | 427 | [[package]] 428 | name = "futures-core" 429 | version = "0.3.31" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 432 | 433 | [[package]] 434 | name = "futures-io" 435 | version = "0.3.31" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 438 | 439 | [[package]] 440 | name = "futures-sink" 441 | version = "0.3.31" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 444 | 445 | [[package]] 446 | name = "futures-task" 447 | version = "0.3.31" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 450 | 451 | [[package]] 452 | name = "futures-util" 453 | version = "0.3.31" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 456 | dependencies = [ 457 | "futures-core", 458 | "futures-sink", 459 | "futures-task", 460 | "pin-project-lite", 461 | "pin-utils", 462 | ] 463 | 464 | [[package]] 465 | name = "getrandom" 466 | version = "0.3.2" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 469 | dependencies = [ 470 | "cfg-if", 471 | "libc", 472 | "r-efi", 473 | "wasi 0.14.2+wasi-0.2.4", 474 | ] 475 | 476 | [[package]] 477 | name = "gimli" 478 | version = "0.31.1" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 481 | 482 | [[package]] 483 | name = "half" 484 | version = "2.5.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" 487 | dependencies = [ 488 | "cfg-if", 489 | "crunchy", 490 | ] 491 | 492 | [[package]] 493 | name = "hashbrown" 494 | version = "0.15.2" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 497 | 498 | [[package]] 499 | name = "heck" 500 | version = "0.5.0" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 503 | 504 | [[package]] 505 | name = "http" 506 | version = "1.3.1" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 509 | dependencies = [ 510 | "bytes", 511 | "fnv", 512 | "itoa", 513 | ] 514 | 515 | [[package]] 516 | name = "http-body" 517 | version = "1.0.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 520 | dependencies = [ 521 | "bytes", 522 | "http", 523 | ] 524 | 525 | [[package]] 526 | name = "http-body-util" 527 | version = "0.1.3" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 530 | dependencies = [ 531 | "bytes", 532 | "futures-core", 533 | "http", 534 | "http-body", 535 | "pin-project-lite", 536 | ] 537 | 538 | [[package]] 539 | name = "httparse" 540 | version = "1.10.1" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 543 | 544 | [[package]] 545 | name = "hyper" 546 | version = "1.6.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 549 | dependencies = [ 550 | "bytes", 551 | "futures-channel", 552 | "futures-util", 553 | "http", 554 | "http-body", 555 | "httparse", 556 | "itoa", 557 | "pin-project-lite", 558 | "smallvec", 559 | "tokio", 560 | "want", 561 | ] 562 | 563 | [[package]] 564 | name = "hyper-tls" 565 | version = "0.6.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 568 | dependencies = [ 569 | "bytes", 570 | "http-body-util", 571 | "hyper", 572 | "hyper-util", 573 | "native-tls", 574 | "tokio", 575 | "tokio-native-tls", 576 | "tower-service", 577 | ] 578 | 579 | [[package]] 580 | name = "hyper-util" 581 | version = "0.1.10" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 584 | dependencies = [ 585 | "bytes", 586 | "futures-channel", 587 | "futures-util", 588 | "http", 589 | "http-body", 590 | "hyper", 591 | "pin-project-lite", 592 | "socket2", 593 | "tokio", 594 | "tower-service", 595 | "tracing", 596 | ] 597 | 598 | [[package]] 599 | name = "icu_collections" 600 | version = "1.5.0" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 603 | dependencies = [ 604 | "displaydoc", 605 | "yoke", 606 | "zerofrom", 607 | "zerovec", 608 | ] 609 | 610 | [[package]] 611 | name = "icu_locid" 612 | version = "1.5.0" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 615 | dependencies = [ 616 | "displaydoc", 617 | "litemap", 618 | "tinystr", 619 | "writeable", 620 | "zerovec", 621 | ] 622 | 623 | [[package]] 624 | name = "icu_locid_transform" 625 | version = "1.5.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 628 | dependencies = [ 629 | "displaydoc", 630 | "icu_locid", 631 | "icu_locid_transform_data", 632 | "icu_provider", 633 | "tinystr", 634 | "zerovec", 635 | ] 636 | 637 | [[package]] 638 | name = "icu_locid_transform_data" 639 | version = "1.5.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 642 | 643 | [[package]] 644 | name = "icu_normalizer" 645 | version = "1.5.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 648 | dependencies = [ 649 | "displaydoc", 650 | "icu_collections", 651 | "icu_normalizer_data", 652 | "icu_properties", 653 | "icu_provider", 654 | "smallvec", 655 | "utf16_iter", 656 | "utf8_iter", 657 | "write16", 658 | "zerovec", 659 | ] 660 | 661 | [[package]] 662 | name = "icu_normalizer_data" 663 | version = "1.5.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 666 | 667 | [[package]] 668 | name = "icu_properties" 669 | version = "1.5.1" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 672 | dependencies = [ 673 | "displaydoc", 674 | "icu_collections", 675 | "icu_locid_transform", 676 | "icu_properties_data", 677 | "icu_provider", 678 | "tinystr", 679 | "zerovec", 680 | ] 681 | 682 | [[package]] 683 | name = "icu_properties_data" 684 | version = "1.5.0" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 687 | 688 | [[package]] 689 | name = "icu_provider" 690 | version = "1.5.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 693 | dependencies = [ 694 | "displaydoc", 695 | "icu_locid", 696 | "icu_provider_macros", 697 | "stable_deref_trait", 698 | "tinystr", 699 | "writeable", 700 | "yoke", 701 | "zerofrom", 702 | "zerovec", 703 | ] 704 | 705 | [[package]] 706 | name = "icu_provider_macros" 707 | version = "1.5.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 710 | dependencies = [ 711 | "proc-macro2", 712 | "quote", 713 | "syn", 714 | ] 715 | 716 | [[package]] 717 | name = "idna" 718 | version = "1.0.3" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 721 | dependencies = [ 722 | "idna_adapter", 723 | "smallvec", 724 | "utf8_iter", 725 | ] 726 | 727 | [[package]] 728 | name = "idna_adapter" 729 | version = "1.2.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 732 | dependencies = [ 733 | "icu_normalizer", 734 | "icu_properties", 735 | ] 736 | 737 | [[package]] 738 | name = "indexmap" 739 | version = "2.8.0" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 742 | dependencies = [ 743 | "equivalent", 744 | "hashbrown", 745 | ] 746 | 747 | [[package]] 748 | name = "ipnet" 749 | version = "2.11.0" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 752 | 753 | [[package]] 754 | name = "is_terminal_polyfill" 755 | version = "1.70.1" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 758 | 759 | [[package]] 760 | name = "itertools" 761 | version = "0.10.5" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 764 | dependencies = [ 765 | "either", 766 | ] 767 | 768 | [[package]] 769 | name = "itertools" 770 | version = "0.13.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 773 | dependencies = [ 774 | "either", 775 | ] 776 | 777 | [[package]] 778 | name = "itoa" 779 | version = "1.0.15" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 782 | 783 | [[package]] 784 | name = "js-sys" 785 | version = "0.3.77" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 788 | dependencies = [ 789 | "once_cell", 790 | "wasm-bindgen", 791 | ] 792 | 793 | [[package]] 794 | name = "libc" 795 | version = "0.2.171" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 798 | 799 | [[package]] 800 | name = "linux-raw-sys" 801 | version = "0.9.3" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" 804 | 805 | [[package]] 806 | name = "litemap" 807 | version = "0.7.5" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 810 | 811 | [[package]] 812 | name = "log" 813 | version = "0.4.26" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 816 | 817 | [[package]] 818 | name = "memchr" 819 | version = "2.7.4" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 822 | 823 | [[package]] 824 | name = "mime" 825 | version = "0.3.17" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 828 | 829 | [[package]] 830 | name = "miniz_oxide" 831 | version = "0.8.5" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 834 | dependencies = [ 835 | "adler2", 836 | ] 837 | 838 | [[package]] 839 | name = "mio" 840 | version = "1.0.3" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 843 | dependencies = [ 844 | "libc", 845 | "wasi 0.11.0+wasi-snapshot-preview1", 846 | "windows-sys 0.52.0", 847 | ] 848 | 849 | [[package]] 850 | name = "native-tls" 851 | version = "0.2.14" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 854 | dependencies = [ 855 | "libc", 856 | "log", 857 | "openssl", 858 | "openssl-probe", 859 | "openssl-sys", 860 | "schannel", 861 | "security-framework", 862 | "security-framework-sys", 863 | "tempfile", 864 | ] 865 | 866 | [[package]] 867 | name = "num-traits" 868 | version = "0.2.19" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 871 | dependencies = [ 872 | "autocfg", 873 | ] 874 | 875 | [[package]] 876 | name = "nvrs" 877 | version = "0.1.8-pre1" 878 | dependencies = [ 879 | "clap", 880 | "colored", 881 | "criterion", 882 | "futures", 883 | "nvrs", 884 | "regex", 885 | "reqwest", 886 | "serde", 887 | "serde_json", 888 | "thiserror", 889 | "tokio", 890 | "tokio-test", 891 | "toml", 892 | ] 893 | 894 | [[package]] 895 | name = "object" 896 | version = "0.36.7" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 899 | dependencies = [ 900 | "memchr", 901 | ] 902 | 903 | [[package]] 904 | name = "once_cell" 905 | version = "1.21.1" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" 908 | 909 | [[package]] 910 | name = "oorandom" 911 | version = "11.1.5" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" 914 | 915 | [[package]] 916 | name = "openssl" 917 | version = "0.10.72" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" 920 | dependencies = [ 921 | "bitflags", 922 | "cfg-if", 923 | "foreign-types", 924 | "libc", 925 | "once_cell", 926 | "openssl-macros", 927 | "openssl-sys", 928 | ] 929 | 930 | [[package]] 931 | name = "openssl-macros" 932 | version = "0.1.1" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 935 | dependencies = [ 936 | "proc-macro2", 937 | "quote", 938 | "syn", 939 | ] 940 | 941 | [[package]] 942 | name = "openssl-probe" 943 | version = "0.1.6" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 946 | 947 | [[package]] 948 | name = "openssl-sys" 949 | version = "0.9.107" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" 952 | dependencies = [ 953 | "cc", 954 | "libc", 955 | "pkg-config", 956 | "vcpkg", 957 | ] 958 | 959 | [[package]] 960 | name = "percent-encoding" 961 | version = "2.3.1" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 964 | 965 | [[package]] 966 | name = "pin-project-lite" 967 | version = "0.2.16" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 970 | 971 | [[package]] 972 | name = "pin-utils" 973 | version = "0.1.0" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 976 | 977 | [[package]] 978 | name = "pkg-config" 979 | version = "0.3.32" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 982 | 983 | [[package]] 984 | name = "plotters" 985 | version = "0.3.7" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" 988 | dependencies = [ 989 | "num-traits", 990 | "plotters-backend", 991 | "plotters-svg", 992 | "wasm-bindgen", 993 | "web-sys", 994 | ] 995 | 996 | [[package]] 997 | name = "plotters-backend" 998 | version = "0.3.7" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 1001 | 1002 | [[package]] 1003 | name = "plotters-svg" 1004 | version = "0.3.7" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" 1007 | dependencies = [ 1008 | "plotters-backend", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "proc-macro2" 1013 | version = "1.0.94" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1016 | dependencies = [ 1017 | "unicode-ident", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "quote" 1022 | version = "1.0.40" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1025 | dependencies = [ 1026 | "proc-macro2", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "r-efi" 1031 | version = "5.2.0" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1034 | 1035 | [[package]] 1036 | name = "rayon" 1037 | version = "1.10.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1040 | dependencies = [ 1041 | "either", 1042 | "rayon-core", 1043 | ] 1044 | 1045 | [[package]] 1046 | name = "rayon-core" 1047 | version = "1.12.1" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1050 | dependencies = [ 1051 | "crossbeam-deque", 1052 | "crossbeam-utils", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "regex" 1057 | version = "1.11.1" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1060 | dependencies = [ 1061 | "aho-corasick", 1062 | "memchr", 1063 | "regex-automata", 1064 | "regex-syntax", 1065 | ] 1066 | 1067 | [[package]] 1068 | name = "regex-automata" 1069 | version = "0.4.9" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1072 | dependencies = [ 1073 | "aho-corasick", 1074 | "memchr", 1075 | "regex-syntax", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "regex-syntax" 1080 | version = "0.8.5" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1083 | 1084 | [[package]] 1085 | name = "reqwest" 1086 | version = "0.12.15" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 1089 | dependencies = [ 1090 | "base64", 1091 | "bytes", 1092 | "futures-core", 1093 | "futures-util", 1094 | "http", 1095 | "http-body", 1096 | "http-body-util", 1097 | "hyper", 1098 | "hyper-tls", 1099 | "hyper-util", 1100 | "ipnet", 1101 | "js-sys", 1102 | "log", 1103 | "mime", 1104 | "native-tls", 1105 | "once_cell", 1106 | "percent-encoding", 1107 | "pin-project-lite", 1108 | "rustls-pemfile", 1109 | "serde", 1110 | "serde_json", 1111 | "serde_urlencoded", 1112 | "sync_wrapper", 1113 | "tokio", 1114 | "tokio-native-tls", 1115 | "tower", 1116 | "tower-service", 1117 | "url", 1118 | "wasm-bindgen", 1119 | "wasm-bindgen-futures", 1120 | "web-sys", 1121 | "windows-registry", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "rustc-demangle" 1126 | version = "0.1.24" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1129 | 1130 | [[package]] 1131 | name = "rustix" 1132 | version = "1.0.2" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" 1135 | dependencies = [ 1136 | "bitflags", 1137 | "errno", 1138 | "libc", 1139 | "linux-raw-sys", 1140 | "windows-sys 0.59.0", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "rustls-pemfile" 1145 | version = "2.2.0" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1148 | dependencies = [ 1149 | "rustls-pki-types", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "rustls-pki-types" 1154 | version = "1.11.0" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 1157 | 1158 | [[package]] 1159 | name = "rustversion" 1160 | version = "1.0.20" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1163 | 1164 | [[package]] 1165 | name = "ryu" 1166 | version = "1.0.20" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1169 | 1170 | [[package]] 1171 | name = "same-file" 1172 | version = "1.0.6" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1175 | dependencies = [ 1176 | "winapi-util", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "schannel" 1181 | version = "0.1.27" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1184 | dependencies = [ 1185 | "windows-sys 0.59.0", 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "security-framework" 1190 | version = "2.11.1" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1193 | dependencies = [ 1194 | "bitflags", 1195 | "core-foundation", 1196 | "core-foundation-sys", 1197 | "libc", 1198 | "security-framework-sys", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "security-framework-sys" 1203 | version = "2.14.0" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1206 | dependencies = [ 1207 | "core-foundation-sys", 1208 | "libc", 1209 | ] 1210 | 1211 | [[package]] 1212 | name = "serde" 1213 | version = "1.0.219" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1216 | dependencies = [ 1217 | "serde_derive", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "serde_derive" 1222 | version = "1.0.219" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1225 | dependencies = [ 1226 | "proc-macro2", 1227 | "quote", 1228 | "syn", 1229 | ] 1230 | 1231 | [[package]] 1232 | name = "serde_json" 1233 | version = "1.0.140" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1236 | dependencies = [ 1237 | "itoa", 1238 | "memchr", 1239 | "ryu", 1240 | "serde", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "serde_spanned" 1245 | version = "0.6.8" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 1248 | dependencies = [ 1249 | "serde", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "serde_urlencoded" 1254 | version = "0.7.1" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1257 | dependencies = [ 1258 | "form_urlencoded", 1259 | "itoa", 1260 | "ryu", 1261 | "serde", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "shlex" 1266 | version = "1.3.0" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1269 | 1270 | [[package]] 1271 | name = "smallvec" 1272 | version = "1.14.0" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 1275 | 1276 | [[package]] 1277 | name = "socket2" 1278 | version = "0.5.8" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1281 | dependencies = [ 1282 | "libc", 1283 | "windows-sys 0.52.0", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "stable_deref_trait" 1288 | version = "1.2.0" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1291 | 1292 | [[package]] 1293 | name = "syn" 1294 | version = "2.0.100" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1297 | dependencies = [ 1298 | "proc-macro2", 1299 | "quote", 1300 | "unicode-ident", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "sync_wrapper" 1305 | version = "1.0.2" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1308 | dependencies = [ 1309 | "futures-core", 1310 | ] 1311 | 1312 | [[package]] 1313 | name = "synstructure" 1314 | version = "0.13.1" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1317 | dependencies = [ 1318 | "proc-macro2", 1319 | "quote", 1320 | "syn", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "tempfile" 1325 | version = "3.19.0" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" 1328 | dependencies = [ 1329 | "fastrand", 1330 | "getrandom", 1331 | "once_cell", 1332 | "rustix", 1333 | "windows-sys 0.59.0", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "thiserror" 1338 | version = "2.0.12" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1341 | dependencies = [ 1342 | "thiserror-impl", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "thiserror-impl" 1347 | version = "2.0.12" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1350 | dependencies = [ 1351 | "proc-macro2", 1352 | "quote", 1353 | "syn", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "tinystr" 1358 | version = "0.7.6" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1361 | dependencies = [ 1362 | "displaydoc", 1363 | "zerovec", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "tinytemplate" 1368 | version = "1.2.1" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 1371 | dependencies = [ 1372 | "serde", 1373 | "serde_json", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "tokio" 1378 | version = "1.45.1" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 1381 | dependencies = [ 1382 | "backtrace", 1383 | "bytes", 1384 | "libc", 1385 | "mio", 1386 | "pin-project-lite", 1387 | "socket2", 1388 | "tokio-macros", 1389 | "windows-sys 0.52.0", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "tokio-macros" 1394 | version = "2.5.0" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1397 | dependencies = [ 1398 | "proc-macro2", 1399 | "quote", 1400 | "syn", 1401 | ] 1402 | 1403 | [[package]] 1404 | name = "tokio-native-tls" 1405 | version = "0.3.1" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1408 | dependencies = [ 1409 | "native-tls", 1410 | "tokio", 1411 | ] 1412 | 1413 | [[package]] 1414 | name = "tokio-stream" 1415 | version = "0.1.17" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 1418 | dependencies = [ 1419 | "futures-core", 1420 | "pin-project-lite", 1421 | "tokio", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "tokio-test" 1426 | version = "0.4.4" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" 1429 | dependencies = [ 1430 | "async-stream", 1431 | "bytes", 1432 | "futures-core", 1433 | "tokio", 1434 | "tokio-stream", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "toml" 1439 | version = "0.8.22" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 1442 | dependencies = [ 1443 | "serde", 1444 | "serde_spanned", 1445 | "toml_datetime", 1446 | "toml_edit", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "toml_datetime" 1451 | version = "0.6.9" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 1454 | dependencies = [ 1455 | "serde", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "toml_edit" 1460 | version = "0.22.26" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 1463 | dependencies = [ 1464 | "indexmap", 1465 | "serde", 1466 | "serde_spanned", 1467 | "toml_datetime", 1468 | "toml_write", 1469 | "winnow", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "toml_write" 1474 | version = "0.1.1" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 1477 | 1478 | [[package]] 1479 | name = "tower" 1480 | version = "0.5.2" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1483 | dependencies = [ 1484 | "futures-core", 1485 | "futures-util", 1486 | "pin-project-lite", 1487 | "sync_wrapper", 1488 | "tokio", 1489 | "tower-layer", 1490 | "tower-service", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "tower-layer" 1495 | version = "0.3.3" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1498 | 1499 | [[package]] 1500 | name = "tower-service" 1501 | version = "0.3.3" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1504 | 1505 | [[package]] 1506 | name = "tracing" 1507 | version = "0.1.41" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1510 | dependencies = [ 1511 | "pin-project-lite", 1512 | "tracing-core", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "tracing-core" 1517 | version = "0.1.33" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1520 | dependencies = [ 1521 | "once_cell", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "try-lock" 1526 | version = "0.2.5" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1529 | 1530 | [[package]] 1531 | name = "unicode-ident" 1532 | version = "1.0.18" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1535 | 1536 | [[package]] 1537 | name = "url" 1538 | version = "2.5.4" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1541 | dependencies = [ 1542 | "form_urlencoded", 1543 | "idna", 1544 | "percent-encoding", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "utf16_iter" 1549 | version = "1.0.5" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1552 | 1553 | [[package]] 1554 | name = "utf8_iter" 1555 | version = "1.0.4" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1558 | 1559 | [[package]] 1560 | name = "utf8parse" 1561 | version = "0.2.2" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1564 | 1565 | [[package]] 1566 | name = "vcpkg" 1567 | version = "0.2.15" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1570 | 1571 | [[package]] 1572 | name = "walkdir" 1573 | version = "2.5.0" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1576 | dependencies = [ 1577 | "same-file", 1578 | "winapi-util", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "want" 1583 | version = "0.3.1" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1586 | dependencies = [ 1587 | "try-lock", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "wasi" 1592 | version = "0.11.0+wasi-snapshot-preview1" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1595 | 1596 | [[package]] 1597 | name = "wasi" 1598 | version = "0.14.2+wasi-0.2.4" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1601 | dependencies = [ 1602 | "wit-bindgen-rt", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "wasm-bindgen" 1607 | version = "0.2.100" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1610 | dependencies = [ 1611 | "cfg-if", 1612 | "once_cell", 1613 | "rustversion", 1614 | "wasm-bindgen-macro", 1615 | ] 1616 | 1617 | [[package]] 1618 | name = "wasm-bindgen-backend" 1619 | version = "0.2.100" 1620 | source = "registry+https://github.com/rust-lang/crates.io-index" 1621 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1622 | dependencies = [ 1623 | "bumpalo", 1624 | "log", 1625 | "proc-macro2", 1626 | "quote", 1627 | "syn", 1628 | "wasm-bindgen-shared", 1629 | ] 1630 | 1631 | [[package]] 1632 | name = "wasm-bindgen-futures" 1633 | version = "0.4.50" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1636 | dependencies = [ 1637 | "cfg-if", 1638 | "js-sys", 1639 | "once_cell", 1640 | "wasm-bindgen", 1641 | "web-sys", 1642 | ] 1643 | 1644 | [[package]] 1645 | name = "wasm-bindgen-macro" 1646 | version = "0.2.100" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1649 | dependencies = [ 1650 | "quote", 1651 | "wasm-bindgen-macro-support", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "wasm-bindgen-macro-support" 1656 | version = "0.2.100" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1659 | dependencies = [ 1660 | "proc-macro2", 1661 | "quote", 1662 | "syn", 1663 | "wasm-bindgen-backend", 1664 | "wasm-bindgen-shared", 1665 | ] 1666 | 1667 | [[package]] 1668 | name = "wasm-bindgen-shared" 1669 | version = "0.2.100" 1670 | source = "registry+https://github.com/rust-lang/crates.io-index" 1671 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1672 | dependencies = [ 1673 | "unicode-ident", 1674 | ] 1675 | 1676 | [[package]] 1677 | name = "web-sys" 1678 | version = "0.3.77" 1679 | source = "registry+https://github.com/rust-lang/crates.io-index" 1680 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1681 | dependencies = [ 1682 | "js-sys", 1683 | "wasm-bindgen", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "winapi-util" 1688 | version = "0.1.9" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1691 | dependencies = [ 1692 | "windows-sys 0.59.0", 1693 | ] 1694 | 1695 | [[package]] 1696 | name = "windows-link" 1697 | version = "0.1.0" 1698 | source = "registry+https://github.com/rust-lang/crates.io-index" 1699 | checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" 1700 | 1701 | [[package]] 1702 | name = "windows-registry" 1703 | version = "0.4.0" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 1706 | dependencies = [ 1707 | "windows-result", 1708 | "windows-strings", 1709 | "windows-targets 0.53.0", 1710 | ] 1711 | 1712 | [[package]] 1713 | name = "windows-result" 1714 | version = "0.3.1" 1715 | source = "registry+https://github.com/rust-lang/crates.io-index" 1716 | checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" 1717 | dependencies = [ 1718 | "windows-link", 1719 | ] 1720 | 1721 | [[package]] 1722 | name = "windows-strings" 1723 | version = "0.3.1" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 1726 | dependencies = [ 1727 | "windows-link", 1728 | ] 1729 | 1730 | [[package]] 1731 | name = "windows-sys" 1732 | version = "0.52.0" 1733 | source = "registry+https://github.com/rust-lang/crates.io-index" 1734 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1735 | dependencies = [ 1736 | "windows-targets 0.52.6", 1737 | ] 1738 | 1739 | [[package]] 1740 | name = "windows-sys" 1741 | version = "0.59.0" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1744 | dependencies = [ 1745 | "windows-targets 0.52.6", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "windows-targets" 1750 | version = "0.52.6" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1753 | dependencies = [ 1754 | "windows_aarch64_gnullvm 0.52.6", 1755 | "windows_aarch64_msvc 0.52.6", 1756 | "windows_i686_gnu 0.52.6", 1757 | "windows_i686_gnullvm 0.52.6", 1758 | "windows_i686_msvc 0.52.6", 1759 | "windows_x86_64_gnu 0.52.6", 1760 | "windows_x86_64_gnullvm 0.52.6", 1761 | "windows_x86_64_msvc 0.52.6", 1762 | ] 1763 | 1764 | [[package]] 1765 | name = "windows-targets" 1766 | version = "0.53.0" 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" 1768 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 1769 | dependencies = [ 1770 | "windows_aarch64_gnullvm 0.53.0", 1771 | "windows_aarch64_msvc 0.53.0", 1772 | "windows_i686_gnu 0.53.0", 1773 | "windows_i686_gnullvm 0.53.0", 1774 | "windows_i686_msvc 0.53.0", 1775 | "windows_x86_64_gnu 0.53.0", 1776 | "windows_x86_64_gnullvm 0.53.0", 1777 | "windows_x86_64_msvc 0.53.0", 1778 | ] 1779 | 1780 | [[package]] 1781 | name = "windows_aarch64_gnullvm" 1782 | version = "0.52.6" 1783 | source = "registry+https://github.com/rust-lang/crates.io-index" 1784 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1785 | 1786 | [[package]] 1787 | name = "windows_aarch64_gnullvm" 1788 | version = "0.53.0" 1789 | source = "registry+https://github.com/rust-lang/crates.io-index" 1790 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 1791 | 1792 | [[package]] 1793 | name = "windows_aarch64_msvc" 1794 | version = "0.52.6" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1797 | 1798 | [[package]] 1799 | name = "windows_aarch64_msvc" 1800 | version = "0.53.0" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 1803 | 1804 | [[package]] 1805 | name = "windows_i686_gnu" 1806 | version = "0.52.6" 1807 | source = "registry+https://github.com/rust-lang/crates.io-index" 1808 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1809 | 1810 | [[package]] 1811 | name = "windows_i686_gnu" 1812 | version = "0.53.0" 1813 | source = "registry+https://github.com/rust-lang/crates.io-index" 1814 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 1815 | 1816 | [[package]] 1817 | name = "windows_i686_gnullvm" 1818 | version = "0.52.6" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1821 | 1822 | [[package]] 1823 | name = "windows_i686_gnullvm" 1824 | version = "0.53.0" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 1827 | 1828 | [[package]] 1829 | name = "windows_i686_msvc" 1830 | version = "0.52.6" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1833 | 1834 | [[package]] 1835 | name = "windows_i686_msvc" 1836 | version = "0.53.0" 1837 | source = "registry+https://github.com/rust-lang/crates.io-index" 1838 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 1839 | 1840 | [[package]] 1841 | name = "windows_x86_64_gnu" 1842 | version = "0.52.6" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1845 | 1846 | [[package]] 1847 | name = "windows_x86_64_gnu" 1848 | version = "0.53.0" 1849 | source = "registry+https://github.com/rust-lang/crates.io-index" 1850 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 1851 | 1852 | [[package]] 1853 | name = "windows_x86_64_gnullvm" 1854 | version = "0.52.6" 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" 1856 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1857 | 1858 | [[package]] 1859 | name = "windows_x86_64_gnullvm" 1860 | version = "0.53.0" 1861 | source = "registry+https://github.com/rust-lang/crates.io-index" 1862 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 1863 | 1864 | [[package]] 1865 | name = "windows_x86_64_msvc" 1866 | version = "0.52.6" 1867 | source = "registry+https://github.com/rust-lang/crates.io-index" 1868 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1869 | 1870 | [[package]] 1871 | name = "windows_x86_64_msvc" 1872 | version = "0.53.0" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 1875 | 1876 | [[package]] 1877 | name = "winnow" 1878 | version = "0.7.7" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" 1881 | dependencies = [ 1882 | "memchr", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "wit-bindgen-rt" 1887 | version = "0.39.0" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1890 | dependencies = [ 1891 | "bitflags", 1892 | ] 1893 | 1894 | [[package]] 1895 | name = "write16" 1896 | version = "1.0.0" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1899 | 1900 | [[package]] 1901 | name = "writeable" 1902 | version = "0.5.5" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1905 | 1906 | [[package]] 1907 | name = "yoke" 1908 | version = "0.7.5" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1911 | dependencies = [ 1912 | "serde", 1913 | "stable_deref_trait", 1914 | "yoke-derive", 1915 | "zerofrom", 1916 | ] 1917 | 1918 | [[package]] 1919 | name = "yoke-derive" 1920 | version = "0.7.5" 1921 | source = "registry+https://github.com/rust-lang/crates.io-index" 1922 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 1923 | dependencies = [ 1924 | "proc-macro2", 1925 | "quote", 1926 | "syn", 1927 | "synstructure", 1928 | ] 1929 | 1930 | [[package]] 1931 | name = "zerofrom" 1932 | version = "0.1.6" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1935 | dependencies = [ 1936 | "zerofrom-derive", 1937 | ] 1938 | 1939 | [[package]] 1940 | name = "zerofrom-derive" 1941 | version = "0.1.6" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 1944 | dependencies = [ 1945 | "proc-macro2", 1946 | "quote", 1947 | "syn", 1948 | "synstructure", 1949 | ] 1950 | 1951 | [[package]] 1952 | name = "zerovec" 1953 | version = "0.10.4" 1954 | source = "registry+https://github.com/rust-lang/crates.io-index" 1955 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1956 | dependencies = [ 1957 | "yoke", 1958 | "zerofrom", 1959 | "zerovec-derive", 1960 | ] 1961 | 1962 | [[package]] 1963 | name = "zerovec-derive" 1964 | version = "0.10.3" 1965 | source = "registry+https://github.com/rust-lang/crates.io-index" 1966 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1967 | dependencies = [ 1968 | "proc-macro2", 1969 | "quote", 1970 | "syn", 1971 | ] 1972 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nvrs" 3 | version = "0.1.8-pre1" 4 | authors = ["Adam Perkowski "] 5 | license = "MIT" 6 | description = "🚦 fast new version checker for software releases 🦀" 7 | repository = "https://github.com/adamperkowski/nvrs" 8 | readme = "README.md" 9 | categories = ["command-line-interface", "command-line-utilities"] 10 | edition = "2024" 11 | include = [ 12 | "**/*.rs", 13 | "src/**/*", 14 | "Cargo.toml", 15 | "README.md", 16 | "LICENSE", 17 | "CHANGELOG.md", 18 | "CODE_OF_CONDUCT.md", 19 | "CONTRIBUTING.md", 20 | "rustfmt.toml", 21 | "man/nvrs.1" 22 | ] 23 | 24 | [features] 25 | cli = ["clap", "colored", "futures"] 26 | default = ["aur", "crates-io", "gitea", "github", "gitlab", "regex"] 27 | aur = [] 28 | crates-io = [] 29 | gitea = [] 30 | github = [] 31 | gitlab = [] 32 | regex = ["dep:regex"] 33 | 34 | [[bin]] 35 | name = "nvrs" 36 | path = "src/cli/main.rs" 37 | required-features = ["cli"] 38 | 39 | [dependencies] 40 | clap = { version = "4.5.38", features = ["derive", "color", "error-context", "help", "std", "usage"], default-features = false , optional = true } 41 | colored = { version = "3.0.0", optional = true } 42 | futures = { version = "0.3.31", default-features = false, optional = true } 43 | regex = { version = "1.11.1", features = ["perf", "unicode"], default-features = false, optional = true } 44 | reqwest = { version = "0.12.15", features = ["default-tls", "json"], default-features = false } 45 | serde = { version = "1.0.219", features = ["derive"], default-features = false } 46 | serde_json = { version = "1.0.140", default-features = false } 47 | thiserror = { version = "2.0.12", default-features = false } 48 | tokio = { version = "1.45.1", features = ["rt-multi-thread", "fs", "io-std", "macros"], default-features = false } 49 | toml = { version = "0.8.22", features = ["parse", "display"], default-features = false } 50 | 51 | [dev-dependencies] 52 | tokio-test = "0.4.4" 53 | criterion = { version = "0.6.0", features = ["html_reports"] } 54 | nvrs = { path = "." } 55 | 56 | [profile.release] 57 | lto = "fat" 58 | codegen-units = 2 59 | panic = "abort" 60 | strip = true 61 | opt-level = "z" 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Adam Perkowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # nvrs 4 | 🚦 fast new version checker for software releases 🦀 5 | 6 | [![Grind Compliant](https://img.shields.io/badge/Grind-Compliant-blue?style=for-the-badge&labelColor=%23a8127d&color=%23336795)](https://github.com/The-Grindhouse/guidelines)
7 | ![Build Status](https://img.shields.io/github/actions/workflow/status/adamperkowski/nvrs/rust.yml?style=for-the-badge&labelColor=%23a8127d&color=%23336795) [![docs.rs](https://img.shields.io/docsrs/nvrs?style=for-the-badge&labelColor=%23a8127d&color=%23336795)](#documentation)
8 | [![GitHub Contributors](https://img.shields.io/github/contributors-anon/adamperkowski/nvrs?style=for-the-badge&labelColor=%23a8127d&color=%23336795)](https://github.com/adamperkowski/nvrs/graphs/contributors) ![GitHub Repo Size](https://img.shields.io/github/repo-size/adamperkowski/nvrs?style=for-the-badge&labelColor=%23a8127d&color=%23336795) ![Repo Created At](https://img.shields.io/github/created-at/adamperkowski/nvrs?style=for-the-badge&labelColor=%23a8127d&color=%23336795) 9 | 10 | ![banner](/banner.webp) 11 | 12 |
13 | 14 | ## Features 15 | ### [nvchecker](https://github.com/lilydjwg/nvchecker) compatibility 16 | check the [release notes](https://github.com/adamperkowski/nvrs/releases) and [configuration docs](#configuration) for compatibility updates and instructions. 17 | 18 | ### Speed 19 | ka-chow 20 | 21 | | command | time per **updated** package | details | 22 | |---------------|------------------------------|--------------------------------------------------------| 23 | | `nvrs` | ~ 0.03s | **API requests included**
depends on internet speed | 24 | | `nvrs --cmp` | ~ 0.0008s | depends on disk speed | 25 | | `nvrs --take` | ~ 0.001s | depends on disk speed | 26 | 27 | ### Sources 28 | - `aur` 29 | - `cratesio` 30 | - `gitea` 31 | - `github` 32 | - `gitlab` (with custom hosts) 33 | - `website` (regex) 34 | 35 | ### QOL improvements 36 | - `ALL` argument for the `--take` command 37 | - `--no-fail` flag to prevent exiting on recoverable errors 38 | - `--nuke` command to delete packages from all files 39 | - `--list-sources` command to list all available sources 40 | 41 | ## Installation 42 | Packaging status 43 | 44 | see the [installation guide](https://nvrs.adamperkowski.dev/installation.html) for instructions on how to install nvrs. 45 | 46 | ## Usage 47 | nvrs relies on a configuration file. see [configuration](https://nvrs.adamperkowski.dev/configuration.html). 48 | 49 | Packaging status 50 | 51 | the core commands are: 52 | - `nvrs` - fetches latest versions of defined packages 53 | - `nvrs --cmp` - compares newver with oldver and displays differences 54 | - `nvrs --take` - automatically updates oldver. takes in a comma-separated list of package names (`ALL` for all packages) 55 | - `nvrs --nuke` - deletes packages from all files. takes in a comma-separated list of names (yes, just like a hitman) 56 | - the `--no-fail` flag - as the name suggests, specifying this will make nvrs not exit on recoverable errors 57 | 58 | ### Example usage 59 | ```sh 60 | # download the example configuration file 61 | curl -L 'https://github.com/adamperkowski/nvrs/raw/main/nvrs.toml' -o nvrs.toml 62 | 63 | # fetch latest package versions (should return `NONE -> version` for all packages) 64 | nvrs --no-fail 65 | 66 | # compare them to latest known versions (should also return `NONE -> version`) 67 | nvrs -c 68 | 69 | # update the known versions 70 | nvrs -t ALL 71 | ``` 72 | 73 | for all available commands, options and flags, see `nvrs --help` and the [manual page](/man/nvrs.1). 74 | 75 | ## Documentation 76 | full documentation can be found at [nvrs.adamperkowski.dev](https://nvrs.adamperkowski.dev). 77 | nvrs library documentation can be found at [docs.rs/nvrs](https://docs.rs/nvrs/latest/nvrs) 78 | 79 | ## Contributing 80 | if you want to contribute to the project, please read the [Contributing Guidelines](/CONTRIBUTING.md) before doing so. 81 | 82 | if you find any parts of the code or the documentation unclear, or have any suggestions, feel free to [open an issue](https://github.com/adamperkowski/nvrs/issues/new/choose) or a [pull request](https://github.com/adamperkowski/nvrs/pull/new). 83 | 84 | ## Credits 85 | - [依云](https://github.com/lilydjwg) | the original [nvchecker](https://github.com/lilydjwg/nvchecker) 86 | - [orhun](https://github.com/orhun) | the idea 87 | 88 |
89 | 90 | Copyright (c) 2025 Adam Perkowski
see [LICENSE](/LICENSE)
91 | 92 |
93 | -------------------------------------------------------------------------------- /banner.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamperkowski/nvrs/914d63468392c074f569b762dfd19ae748dc6586/banner.webp -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Adam Perkowski"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "nvrs" 7 | 8 | [output.html] 9 | cname = "nvrs.adamperkowski.dev" 10 | default-theme = "nvrs" 11 | preferred-dark-theme = "nvrs" 12 | git-repository-url = "https://github.com/adamperkowski/nvrs" 13 | edit-url-template = "https://github.com/adamperkowski/nvrs/edit/main/book/{path}" 14 | additional-css = ["./theme/custom.css"] 15 | 16 | [output.html.search] 17 | use-boolean-and = true 18 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [nvrs](./nvrs.md) 4 | - [features](./features.md) 5 | - [installation](./installation.md) 6 | - [configuration](./configuration.md) 7 | - [`__config__` table](./config-table.md) 8 | - [package entries](./package-entries.md) 9 | - [keyfile structure](./keyfile-structure.md) 10 | - [credits](./credits.md) 11 | -------------------------------------------------------------------------------- /book/src/config-table.md: -------------------------------------------------------------------------------- 1 | # `__config__` table 2 | this configures the behavior of nvrs. see the [example config](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml#L7-L10). 3 | 4 | available fields: 5 | 6 | | name | description | type | required | 7 | |-----------|----------------------------------------------------------------------|--------|----------| 8 | | `oldver` | path to the `oldver` file | string | ✔️ | 9 | | `newver` | path to the `newver` file | string | ✔️ | 10 | | `keyfile` | path to a keyfile (see [keyfile structure](/keyfile-structure.html)) | string | ❌ | 11 | -------------------------------------------------------------------------------- /book/src/configuration.md: -------------------------------------------------------------------------------- 1 | # configuration 2 | nvrs relies on a TOML configuration file ([example](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml)) containing basic settings, such as `oldver`, `newver` & `keyfile` paths, as well as [package entries](/package-entries.md). supported config paths: 3 | - `$XDG_CONFIG_HOME/nvrs.toml` (`~/.config/nvrs.toml` if the variable is not set) 4 | - `./nvrs.toml` 5 | - custom paths set with `nvrs --config` 6 | -------------------------------------------------------------------------------- /book/src/credits.md: -------------------------------------------------------------------------------- 1 | # credits 2 | - [依云](https://github.com/lilydjwg) | the original [nvchecker](https://github.com/lilydjwg/nvchecker) 3 | - [orhun](https://github.com/orhun) | the idea 4 | -------------------------------------------------------------------------------- /book/src/features.md: -------------------------------------------------------------------------------- 1 | # features 2 | ### [nvchecker](https://github.com/lilydjwg/nvchecker) compatibility 3 | check the [release notes](https://github.com/adamperkowski/nvrs/releases) and [configuration docs](/configuration.html) for compatibility updates and instructions 4 | 5 | ### speed 6 | ka-chow 7 | 8 | | command | time per **updated** package | details | 9 | |---------------|------------------------------|--------------------------------------------------------| 10 | | `nvrs` | ~ 0.03s | **API requests included**
depends on internet speed | 11 | | `nvrs --cmp` | ~ 0.0008s | depends on disk speed | 12 | | `nvrs --take` | ~ 0.001s | depends on disk speed | 13 | 14 | ### sources 15 | see the [project's readme](https://github.com/adamperkowski/nvrs?tab=readme-ov-file#sources) for a list of supported sources 16 | -------------------------------------------------------------------------------- /book/src/installation.md: -------------------------------------------------------------------------------- 1 | # installation 2 | Packaging status 3 | 4 | ## Arch Linux 5 | [nvrs](https://aur.archlinux.org/packages/nvrs) is available as a package in the [AUR](https://aur.archlinux.org).
6 | you can install it with your preferred [AUR helper](https://wiki.archlinux.org/title/AUR_helpers), example: 7 | 8 | ```sh 9 | paru -S nvrs 10 | ``` 11 | 12 | or manually: 13 | 14 | ``` 15 | git clone https://aur.archlinux.org/nvrs.git 16 | cd nvrs 17 | makepkg -si 18 | ``` 19 | 20 | ## Cargo 21 | [nvrs](https://crates.io/crates/nvrs) can be installed via [Cargo](https://doc.rust-lang.org/cargo) with: 22 | 23 | ```sh 24 | cargo install nvrs --all-features 25 | ``` 26 | 27 | note that crates installed using `cargo install` require manual updating with `cargo install --force`. 28 | 29 | ## Manual 30 | 1. download the latest binary from [GitHub's release page](https://github.com/adamperkowski/nvrs/releases/latest) 31 | 2. allow execution 32 | ```sh 33 | chmod +x nvrs 34 | ``` 35 | 3. move the file to a directory in `$PATH` (using `/usr/bin` as an example) 36 | ```sh 37 | sudo mv nvrs /usr/bin/nvrs 38 | ``` 39 | -------------------------------------------------------------------------------- /book/src/keyfile-structure.md: -------------------------------------------------------------------------------- 1 | # keyfile structure 2 | this file contains API keys for various [sources](https://github.com/adamperkowski/nvrs?tab=readme-ov-file#sources). example can be found [here](https://github.com/adamperkowski/nvrs/blob/main/n_keyfile.toml). 3 | 4 | ```toml 5 | [keys] 6 | github = "your_secret_github_api_key_that_you_shouldnt_push_to_a_public_nor_a_private_remote_repo_because_there_will_definitely_be_serious_consequences_sooner_or_later_if_you_do_trust_me_just_dont" 7 | gitlab = "remember_to_replace_the_example_values_here_here_with_your_actual_keys_otherwise_it_wont_work_but_dont_push_keyfiles_to_remote_repos" 8 | ``` 9 | 10 | "I think that example value is not long enough" - orhun 11 | -------------------------------------------------------------------------------- /book/src/nvrs.md: -------------------------------------------------------------------------------- 1 | # nvrs 2 | [![Grind Compliant](https://img.shields.io/badge/Grind-Compliant-blue?style=for-the-badge&labelColor=%23a8127d&color=%23336795)](https://github.com/grindhousedev/grindlines) 3 | 4 | fast new version checker for software releases 5 | 6 | documentation for bleeding-edge main can be found at [https://nvrs.adamperkowski.dev/main](https://nvrs.adamperkowski.dev/main) 7 | 8 | keep in mind that this website is still a WIP 9 | 10 | [see at GitHub](https://github.com/adamperkowski/nvrs) 11 | 12 | ## copyright 13 | ``` 14 | MIT License 15 | 16 | Copyright (c) 2025 Adam Perkowski 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | ``` 36 | -------------------------------------------------------------------------------- /book/src/package-entries.md: -------------------------------------------------------------------------------- 1 | # package entries 2 | [example](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml#L12-L15) 3 | 4 | package entries are custom entries in the main config file. they contain values such as: 5 | 6 | | name | description | type | required | custom | 7 | |---------------|---------------------------------------------------------------------------------|--------|----------|--------| 8 | | `source` | see [sources](https://github.com/adamperkowski/nvrs?tab=readme-ov-file#sources) | string | ✔️ | ❌ | 9 | | source name | the "target". eg. repo path for `github` | string | ✔️ | ✔️ | 10 | | `host` | domain name the source is hosted on | string | ❌ | ❌ | 11 | | `prefix` | the prefix used in releases / tags
example: `v` for tags like `v0.1.0` | string | ❌ | ❌ | 12 | | `use_max_tag` | use max git tag instead of the latest release | bool | ❌ | ❌ | 13 | | `url` | url to check for source type `regex` | string | ❌ | ❌ | 14 | | `regex` | regex to search url for source type `regex` | bool | ❌ | ❌ | 15 | -------------------------------------------------------------------------------- /book/theme/custom.css: -------------------------------------------------------------------------------- 1 | /* Globals */ 2 | 3 | :root { 4 | --sidebar-width: 300px; 5 | --sidebar-resize-indicator-width: 8px; 6 | --sidebar-resize-indicator-space: 2px; 7 | --page-padding: 15px; 8 | --content-max-width: 750px; 9 | --menu-bar-height: 50px; 10 | --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; 11 | --code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */ 12 | } 13 | 14 | ::selection { 15 | background: #6E1D57; 16 | color: #00050D; 17 | } 18 | 19 | /* Themes */ 20 | 21 | .nvrs { 22 | --bg: #00050D; 23 | --fg: #A3A3A3; 24 | 25 | --sidebar-bg: #000D19; 26 | --sidebar-fg: #A3A3A3; 27 | --sidebar-non-existant: #A3A3A3; 28 | --sidebar-active: #A01576; 29 | --sidebar-spacer: #00050D; 30 | 31 | --scrollbar: var(--sidebar-fg); 32 | 33 | --icons: #A3A3A3; 34 | --icons-hover: #A3A3A3; 35 | 36 | --links: #1A76A8; 37 | 38 | --inline-code-color: #A3A3A3; 39 | 40 | --theme-popup-bg: #000D19; 41 | --theme-popup-border: #0C567E; 42 | --theme-hover: #6E1D57; 43 | 44 | --quote-bg: #000D19; 45 | --quote-border: #0C567E; 46 | 47 | --warning-border: #7D0C0C; 48 | 49 | --table-border-color: #000D19; 50 | --table-header-bg: #00050D; 51 | --table-alternate-bg: #000D19; 52 | 53 | --searchbar-border-color: #0C567E; 54 | --searchbar-bg: #000D19; 55 | --searchbar-fg: #A3A3A3; 56 | --searchbar-shadow-color: #aaa; 57 | --searchresults-header-fg: #666; 58 | --searchresults-border-color: #98a3ad; 59 | --searchresults-li-bg: #2b2b2f; 60 | --search-mark-bg: #0C567E; 61 | 62 | --color-scheme: dark; 63 | 64 | /* Same as `--icons` */ 65 | --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%); 66 | /* Same as `--sidebar-active` */ 67 | --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%); 68 | } 69 | 70 | /* https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html */ 71 | .nvrs code { 72 | color: #A3A3A3; 73 | background: #000D19; 74 | } 75 | .nvrs.hljs { 76 | color: #A3A3A3; 77 | background: #000D19; 78 | } 79 | .nvrs .hljs-keyword { 80 | color: #6E1D57; 81 | } 82 | .nvrs .hljs-built_in { 83 | color: #951313; 84 | } 85 | .nvrs .hljs-type { 86 | color: #ABA119; 87 | } 88 | .nvrs .hljs-literal { 89 | color: #7D750C; 90 | } 91 | .nvrs .hljs-number { 92 | color: #7D750C; 93 | } 94 | .nvrs .hljs-operator { 95 | color: #188AA9; 96 | } 97 | .nvrs .hljs-punctuation { 98 | color: #A3A3A3; 99 | } 100 | .nvrs .hljs-property { 101 | color: #12748F; 102 | } 103 | .nvrs .hljs-regexp { 104 | color: #A01576; 105 | } 106 | .nvrs .hljs-string { 107 | color: #16A85F; 108 | } 109 | .nvrs .hljs-char.escape_ { 110 | color: #16A85F; 111 | } 112 | .nvrs .hljs-subst { 113 | color: #A3A3A3; 114 | } 115 | .nvrs .hljs-symbol { 116 | color: #ABA119; 117 | } 118 | .nvrs .hljs-variable { 119 | color: #6E1D57; 120 | } 121 | .nvrs .hljs-variable.language_ { 122 | color: #6E1D57; 123 | } 124 | .nvrs .hljs-variable.constant_ { 125 | color: #7D750C; 126 | } 127 | .nvrs .hljs-title { 128 | color: #1A76A8; 129 | } 130 | .nvrs .hljs-title.class_ { 131 | color: #ABA119; 132 | } 133 | .nvrs .hljs-title.function_ { 134 | color: #1A76A8; 135 | } 136 | .nvrs .hljs-params { 137 | color: #A3A3A3; 138 | } 139 | .nvrs .hljs-comment { 140 | color: #A3A3A3; 141 | } 142 | .nvrs .hljs-doctag { 143 | color: #951313; 144 | } 145 | .nvrs .hljs-meta { 146 | color: #7D750C; 147 | } 148 | .nvrs .hljs-section { 149 | color: #0C567E; 150 | } 151 | .nvrs .hljs-tag { 152 | color: #188AA9; 153 | } 154 | .nvrs .hljs-name { 155 | color: #6E1D57; 156 | } 157 | .nvrs .hljs-attr { 158 | color: #12748F; 159 | } 160 | .nvrs .hljs-attribute { 161 | color: #16A85F; 162 | } 163 | .nvrs .hljs-bullet { 164 | color: #188AA9; 165 | } 166 | .nvrs .hljs-code { 167 | color: #16A85F; 168 | } 169 | .nvrs .hljs-emphasis { 170 | color: #951313; 171 | font-style: italic; 172 | } 173 | .nvrs .hljs-strong { 174 | color: #951313; 175 | font-weight: bold; 176 | } 177 | .nvrs .hljs-formula { 178 | color: #188AA9; 179 | } 180 | .nvrs .hljs-link { 181 | color: #12748F; 182 | font-style: italic; 183 | } 184 | .nvrs .hljs-quote { 185 | color: #16A85F; 186 | font-style: italic; 187 | } 188 | .nvrs .hljs-selector-tag { 189 | color: #ABA119; 190 | } 191 | .nvrs .hljs-selector-id { 192 | color: #89b4fa; 193 | } 194 | .nvrs .hljs-selector-class { 195 | color: #0C567E; 196 | } 197 | .nvrs .hljs-selector-attr { 198 | color: #6E1D57; 199 | } 200 | .nvrs .hljs-selector-pseudo { 201 | color: #0C567E; 202 | } 203 | .nvrs .hljs-template-tag { 204 | color: #ABA119; 205 | } 206 | .nvrs .hljs-template-variable { 207 | color: #ABA119; 208 | } 209 | .nvrs .hljs-addition { 210 | color: #16A85F; 211 | background: rgba(166, 227, 161, 0.15); 212 | } 213 | .nvrs .hljs-deletion { 214 | color: #951313; 215 | background: rgba(243, 139, 168, 0.15); 216 | } 217 | .nvrs blockquote blockquote { 218 | border-top: 0.1em solid #A3A3A3; 219 | border-bottom: 0.1em solid #A3A3A3; 220 | } 221 | .nvrs hr { 222 | border-color: #A3A3A3; 223 | border-style: solid; 224 | } 225 | .nvrs del { 226 | color: #A3A3A3; 227 | } 228 | .nvrs .ace_gutter { 229 | color: #A3A3A3; 230 | background: #181825; 231 | } 232 | 233 | 234 | /* https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html */ 235 | .mocha.hljs { 236 | color: #cdd6f4; 237 | background: #1e1e2e; 238 | } 239 | .mocha .hljs-keyword { 240 | color: #cba6f7; 241 | } 242 | .mocha .hljs-built_in { 243 | color: #f38ba8; 244 | } 245 | .mocha .hljs-type { 246 | color: #f9e2af; 247 | } 248 | .mocha .hljs-literal { 249 | color: #fab387; 250 | } 251 | .mocha .hljs-number { 252 | color: #fab387; 253 | } 254 | .mocha .hljs-operator { 255 | color: #89dceb; 256 | } 257 | .mocha .hljs-punctuation { 258 | color: #bac2de; 259 | } 260 | .mocha .hljs-property { 261 | color: #94e2d5; 262 | } 263 | .mocha .hljs-regexp { 264 | color: #f5c2e7; 265 | } 266 | .mocha .hljs-string { 267 | color: #a6e3a1; 268 | } 269 | .mocha .hljs-char.escape_ { 270 | color: #a6e3a1; 271 | } 272 | .mocha .hljs-subst { 273 | color: #a6adc8; 274 | } 275 | .mocha .hljs-symbol { 276 | color: #f2cdcd; 277 | } 278 | .mocha .hljs-variable { 279 | color: #cba6f7; 280 | } 281 | .mocha .hljs-variable.language_ { 282 | color: #cba6f7; 283 | } 284 | .mocha .hljs-variable.constant_ { 285 | color: #fab387; 286 | } 287 | .mocha .hljs-title { 288 | color: #89b4fa; 289 | } 290 | .mocha .hljs-title.class_ { 291 | color: #f9e2af; 292 | } 293 | .mocha .hljs-title.function_ { 294 | color: #89b4fa; 295 | } 296 | .mocha .hljs-params { 297 | color: #cdd6f4; 298 | } 299 | .mocha .hljs-comment { 300 | color: #9399b2; 301 | } 302 | .mocha .hljs-doctag { 303 | color: #f38ba8; 304 | } 305 | .mocha .hljs-meta { 306 | color: #fab387; 307 | } 308 | .mocha .hljs-section { 309 | color: #89b4fa; 310 | } 311 | .mocha .hljs-tag { 312 | color: #94e2d5; 313 | } 314 | .mocha .hljs-name { 315 | color: #cba6f7; 316 | } 317 | .mocha .hljs-attr { 318 | color: #89b4fa; 319 | } 320 | .mocha .hljs-attribute { 321 | color: #a6e3a1; 322 | } 323 | .mocha .hljs-bullet { 324 | color: #94e2d5; 325 | } 326 | .mocha .hljs-code { 327 | color: #a6e3a1; 328 | } 329 | .mocha .hljs-emphasis { 330 | color: #f38ba8; 331 | font-style: italic; 332 | } 333 | .mocha .hljs-strong { 334 | color: #f38ba8; 335 | font-weight: bold; 336 | } 337 | .mocha .hljs-formula { 338 | color: #94e2d5; 339 | } 340 | .mocha .hljs-link { 341 | color: #74c7ec; 342 | font-style: italic; 343 | } 344 | .mocha .hljs-quote { 345 | color: #a6e3a1; 346 | font-style: italic; 347 | } 348 | .mocha .hljs-selector-tag { 349 | color: #f9e2af; 350 | } 351 | .mocha .hljs-selector-id { 352 | color: #89b4fa; 353 | } 354 | .mocha .hljs-selector-class { 355 | color: #94e2d5; 356 | } 357 | .mocha .hljs-selector-attr { 358 | color: #cba6f7; 359 | } 360 | .mocha .hljs-selector-pseudo { 361 | color: #94e2d5; 362 | } 363 | .mocha .hljs-template-tag { 364 | color: #f2cdcd; 365 | } 366 | .mocha .hljs-template-variable { 367 | color: #f2cdcd; 368 | } 369 | .mocha .hljs-addition { 370 | color: #a6e3a1; 371 | background: rgba(166, 227, 161, 0.15); 372 | } 373 | .mocha .hljs-deletion { 374 | color: #f38ba8; 375 | background: rgba(243, 139, 168, 0.15); 376 | } 377 | .mocha code { 378 | color: #cdd6f4; 379 | background: #181825; 380 | } 381 | .mocha blockquote blockquote { 382 | border-top: 0.1em solid #585b70; 383 | border-bottom: 0.1em solid #585b70; 384 | } 385 | .mocha hr { 386 | border-color: #585b70; 387 | border-style: solid; 388 | } 389 | .mocha del { 390 | color: #9399b2; 391 | } 392 | .mocha .ace_gutter { 393 | color: #7f849c; 394 | background: #181825; 395 | } 396 | .mocha .ace_gutter-active-line.ace_gutter-cell { 397 | color: #f5c2e7; 398 | background: #181825; 399 | } 400 | .mocha .tooltiptext { 401 | background: #181825; 402 | color: #cdd6f4; 403 | } 404 | 405 | .mocha { 406 | --bg: #1e1e2e; 407 | --fg: #cdd6f4; 408 | --sidebar-bg: #181825; 409 | --sidebar-fg: #cdd6f4; 410 | --sidebar-non-existant: #6c7086; 411 | --sidebar-active: #89b4fa; 412 | --sidebar-spacer: #6c7086; 413 | --scrollbar: #6c7086; 414 | --icons: #6c7086; 415 | --icons-hover: #7f849c; 416 | --links: #89b4fa; 417 | --inline-code-color: #fab387; 418 | --theme-popup-bg: #181825; 419 | --theme-popup-border: #6c7086; 420 | --theme-hover: #313244; 421 | --quote-bg: #181825; 422 | --quote-border: #11111b; 423 | --table-border-color: #313244; 424 | --table-header-bg: #181825; 425 | --table-alternate-bg: #181825; 426 | --searchbar-border-color: #313244; 427 | --searchbar-bg: #181825; 428 | --searchbar-fg: #cdd6f4; 429 | --searchbar-shadow-color: #11111b; 430 | --searchresults-header-fg: #cdd6f4; 431 | --searchresults-border-color: #313244; 432 | --searchresults-li-bg: #1e1e2e; 433 | --search-mark-bg: #fab387; 434 | --warning-border: #fab387; 435 | --color-scheme: dark; 436 | } 437 | -------------------------------------------------------------------------------- /book/theme/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | {{#if is_print }} 8 | 9 | {{/if}} 10 | {{#if base_url}} 11 | 12 | {{/if}} 13 | 14 | 15 | 16 | {{> head}} 17 | 18 | 19 | 20 | 21 | 22 | {{#if favicon_svg}} 23 | 24 | {{/if}} 25 | {{#if favicon_png}} 26 | 27 | {{/if}} 28 | 29 | 30 | 31 | {{#if print_enable}} 32 | 33 | {{/if}} 34 | 35 | 36 | 37 | {{#if copy_fonts}} 38 | 39 | {{/if}} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{#each additional_css}} 48 | 49 | {{/each}} 50 | 51 | {{#if mathjax_support}} 52 | 53 | 54 | {{/if}} 55 | 56 | 57 | 61 | 62 | 63 | 64 | 65 |
66 | 67 | 81 | 82 | 83 | 92 | 93 | 94 | 95 | 96 | 109 | 110 | 120 | 121 |
122 | 123 |
124 | {{> header}} 125 | 126 | 166 | 167 | {{#if search_enabled}} 168 | 178 | {{/if}} 179 | 180 | 181 | 188 | 189 |
190 |
191 | {{{ content }}} 192 |
193 | 194 | 210 |
211 |
212 | 213 | 226 | 227 |
228 | 229 | {{#if live_reload_endpoint}} 230 | 231 | 246 | {{/if}} 247 | 248 | {{#if google_analytics}} 249 | 250 | 265 | {{/if}} 266 | 267 | {{#if playground_line_numbers}} 268 | 271 | {{/if}} 272 | 273 | {{#if playground_copyable}} 274 | 277 | {{/if}} 278 | 279 | {{#if playground_js}} 280 | 281 | 282 | 283 | 284 | 285 | {{/if}} 286 | 287 | {{#if search_js}} 288 | 289 | 290 | 291 | {{/if}} 292 | 293 | 294 | 295 | 296 | 297 | 298 | {{#each additional_js}} 299 | 300 | {{/each}} 301 | 302 | {{#if is_print}} 303 | {{#if mathjax_support}} 304 | 311 | {{else}} 312 | 317 | {{/if}} 318 | {{/if}} 319 | 320 |
321 | 322 | 323 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # https://git-cliff.org/docs/configuration 2 | 3 | [changelog] 4 | header = """ 5 | # Changelog\n 6 | All notable changes to nvrs will be documented in this file.\n 7 | """ 8 | body = """ 9 | {% if version %}\ 10 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 11 | {% else %}\ 12 | ## [upstream] 13 | {% endif %}\ 14 | {% for group, commits in commits | group_by(attribute="group") %} 15 | ### {{ group | striptags | trim | upper_first }} 16 | {% for commit in commits %} 17 | - {% if commit.scope %}(*{{ commit.scope }}*) {% endif %}\ 18 | {% if commit.breaking %}[**breaking**] {% endif %}\ 19 | {{ commit.message }} ([{{ commit.id | truncate(length=7, end="") }}](/commit/{{ commit.id }}))\ 20 | {% endfor %} 21 | {% endfor %}\n 22 | """ 23 | footer = """ 24 | generated by [git-cliff](https://github.com/orhun/git-cliff) :) 25 | """ 26 | trim = true 27 | postprocessors = [ 28 | { pattern = '', replace = "https://github.com/adamperkowski/nvrs" }, 29 | ] 30 | # render body even when there are no releases to process 31 | # render_always = true 32 | # output file path 33 | # output = "CHANGELOG.md" 34 | 35 | [git] 36 | conventional_commits = true 37 | filter_unconventional = false 38 | # process each line of a commit as an individual commit 39 | split_commits = false 40 | commit_preprocessors = [ 41 | # Replace issue numbers 42 | { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, 43 | { pattern = '\n.*', replace = "" } 44 | # Check spelling of the commit with https://github.com/crate-ci/typos 45 | # If the spelling is incorrect, it will be automatically fixed. 46 | #{ pattern = '.*', replace_command = 'typos --write-changes -' }, 47 | ] 48 | # regex for parsing and grouping commits 49 | commit_parsers = [ 50 | { message = "^feat", group = "🚀 Features" }, 51 | { message = "^fix", group = "🐛 Bug Fixes" }, 52 | { message = "^ui|^ux", group = "🧩 UI/UX" }, 53 | { message = "^doc", group = "📚 Documentation" }, 54 | { message = "^perf", group = "⚡ Performance" }, 55 | { message = "^refact", group = "🛠️ Refactoring" }, 56 | { message = "^test", group = "🧪 Testing" }, 57 | { message = "^chore\\(release\\): prepare for", skip = true }, 58 | { message = "^chore\\(deps.*\\)", skip = true }, 59 | { message = "^chore\\(pr\\)", skip = true }, 60 | { message = "changelog", skip = true }, 61 | { message = "^chore", group = "⚙️ Miscellaneous" }, 62 | { body = ".*security", group = "🛡️ Security" }, 63 | { message = "^revert", group = "◀️ Revert" }, 64 | { body = ".*", group = "Other (unconventional)" }, 65 | ] 66 | # filter out the commits that are not matched by commit parsers 67 | filter_commits = true 68 | # sort the tags topologically 69 | topo_order = false 70 | # sort the commits inside sections by oldest/newest order 71 | sort_commits = "oldest" 72 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | fmt: 2 | cargo fmt --all 3 | 4 | deps: 5 | cargo upgrade 6 | cargo update 7 | 8 | run-cli: 9 | cargo run --bin nvrs --features=cli 10 | 11 | check: 12 | cargo fmt --all --check 13 | cargo clippy --all-features -- -Dwarnings 14 | cargo clippy --all-features --release -- -Dwarnings 15 | 16 | test: check 17 | cargo test --all-features --no-fail-fast 18 | 19 | release ver="": deps test 20 | rm -rf target 21 | 22 | git-cliff --tag v{{ver}} > CHANGELOG.md 23 | 24 | git add Cargo* CHANGELOG.md 25 | git commit -m "chore(release): prepare for v{{ver}}" 26 | git tag -a v{{ver}} -m "v{{ver}}" 27 | 28 | git push origin main --follow-tags 29 | 30 | CARGO_TARGET_DIR=target \ 31 | cargo publish && \ 32 | CARGO_TARGET_DIR=target \ 33 | cargo build --bin nvrs --features=cli --release 34 | 35 | cp target/release/nvrs nvrs 36 | cp .github/SECURITY.md SECURITY.md 37 | tar -czf "nvrs-v{{ver}}.tar.gz" nvrs CHANGELOG.md CONTRIBUTING.md LICENSE README.md SECURITY.md nvrs.toml keyfile.toml man 38 | 39 | clean: 40 | cargo clean 41 | rm -f nvrs nvrs-v*.tar.gz SECURITY.md 42 | -------------------------------------------------------------------------------- /man/nvrs.1: -------------------------------------------------------------------------------- 1 | .\" manpage for nvrs 2 | .TH "nvrs" "1" "January 2025" "" "nvrs manual" 3 | 4 | .SH NAME 5 | nvrs \- fast new version checker for software releases 🚦🦀 6 | 7 | .SH SYNOPSIS 8 | \fBnvrs [OPTIONS]\fR 9 | 10 | .SH DESCRIPTION 11 | nvrs is a new version checker for software releases 12 | .br 13 | nvchecker rewritten in Rust 14 | 15 | .SH OPTIONS 16 | .TP 17 | \fB\-c\fR, \fB\-\-cmp\fR 18 | Compare newver with oldver and display differences as updates 19 | 20 | .TP 21 | \fB\-t\fR, \fB\-\-take\fR \fI\fR 22 | Comma-separated list of packages to update automatically (use `ALL` for all) 23 | 24 | .TP 25 | \fB\-n\fR, \fB\-\-nuke\fR \fI\fR 26 | Comma-separated list of packages to delete from the config 27 | 28 | .TP 29 | \fB\-\-no\-fail\fR 30 | Don't exit the program on recoverable errors 31 | 32 | .TP 33 | \fB\-\-list\-sources\fR 34 | List available sources 35 | 36 | .TP 37 | \fB\-\-config\fR \fI\fR 38 | Override path to the config file 39 | Default paths: 40 | .br 41 | \fI~/.config/nvrs.toml\fR 42 | .br 43 | \fI./nvrs.toml\fR 44 | 45 | .TP 46 | \fB\-\-copyright\fR 47 | Display copyright information 48 | 49 | .TP 50 | \fB\-h\fR, \fB\-\-help\fR 51 | Print help 52 | 53 | .TP 54 | \fB\-V\fR, \fB\-\-version\fB 55 | Print version 56 | 57 | .SH REPORTING BUGS 58 | since the project is still in active development, you may encounter some issues. 59 | .br 60 | please consider submitting feedback if you do. 61 | .br 62 | https://github.com/adamperkowski/nvrs/issues 63 | 64 | .SH COPYRIGHT 65 | Copyright (c) 2025 Adam Perkowski 66 | .br 67 | MIT License 68 | https://opensource.org/license/MIT 69 | 70 | .SH AUTHOR 71 | written by Adam Perkowski 72 | 73 | -------------------------------------------------------------------------------- /n_keyfile.toml: -------------------------------------------------------------------------------- 1 | # this is an example key configuration file for nvrs 2 | # also see `nvrs.toml` 3 | # https://github.com/adamperkowski/nvrs 4 | 5 | [keys] 6 | # github = "[REDACTED]" 7 | # gitlab = "[REDACTED]" 8 | -------------------------------------------------------------------------------- /nvrs.tape: -------------------------------------------------------------------------------- 1 | # VHS 2 | # https://github.com/charmbracelet/vhs 3 | 4 | Output nvrs.gif 5 | 6 | Require nvrs 7 | 8 | Set Shell "zsh" 9 | Set FontSize 32 10 | Set Width 1280 11 | Set Height 720 12 | Set TypingSpeed 0ms 13 | Set Theme { "black": "#000D19", "red": "#7D0C0C", "green": "#0C7D45", "yellow": "#7D750C", "blue": "#0C567E", "magenta": "#6E1D57", "cyan": "#12748F", "white": "#A3A3A3", "brightBlack": "#000D19", "brightRed": "#951313", "brightGreen": "#16A85F", "brightYellow": "#ABA119", "brightBlue": "#1A76A8", "brightMagenta": "#A01576", "brightCyan": "#188AA9", "brightWhite": "#A3A3A3", "background": "#00050D", "foreground": "#A3A3A3", "selection": "#6E1D57", "cursor": "#A3A3A3" } 14 | 15 | Hide 16 | Type "source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh" 17 | Enter 18 | Type "source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh" 19 | Enter 20 | Ctrl+L 21 | Sleep 100ms 22 | Show 23 | 24 | Set TypingSpeed 30ms 25 | 26 | Type "1. fetch latest versions of defined packages" 27 | Sleep 1s 28 | Ctrl+U 29 | Sleep 500ms 30 | Type "nvrs" 31 | Sleep 500ms 32 | Enter 33 | 34 | Sleep 1.5s 35 | Ctrl+L 36 | 37 | Type "2. compare them to latest known versions" 38 | Sleep 1s 39 | Ctrl+U 40 | Sleep 500ms 41 | Type "nvrs --cmp" 42 | Sleep 500ms 43 | Enter 44 | 45 | Sleep 1.5s 46 | Ctrl+L 47 | 48 | Type "3. update the known versions" 49 | Sleep 1s 50 | Ctrl+U 51 | Sleep 500ms 52 | Type "nvrs --take ALL" 53 | Sleep 500ms 54 | Enter 55 | 56 | Sleep 2s 57 | 58 | Enter 59 | Type "4. go on github.com/adamperkowski/nvrs & leave a star" 60 | 61 | Sleep 4s 62 | -------------------------------------------------------------------------------- /nvrs.toml: -------------------------------------------------------------------------------- 1 | # this is an example configuration file for nvrs 2 | # https://github.com/adamperkowski/nvrs 3 | 4 | [__config__] 5 | oldver = "oldver.json" 6 | newver = "newver.json" 7 | keyfile = "n_keyfile.toml" 8 | 9 | [julec] 10 | source = "github" 11 | github = "julelang/jule" 12 | prefix = "jule" 13 | 14 | [comlink] 15 | source = "aur" 16 | aur = "comlink" 17 | 18 | [mkinitcpio] 19 | source = "gitlab" 20 | host = "gitlab.archlinux.org" 21 | gitlab = "archlinux/mkinitcpio/mkinitcpio" 22 | prefix = "v" 23 | 24 | [rustup] 25 | source = "github" 26 | github = "rust-lang/rustup" 27 | use_max_tag = true 28 | 29 | [rustrover] 30 | source = 'regex' 31 | url = 'https://data.services.jetbrains.com/products?code=RR&release.type=release' 32 | encoding = 'utf8' 33 | regex = 'RustRover-([\d.]+).tar.gz' 34 | 35 | [linux] 36 | source = 'regex' 37 | url = 'https://www.kernel.org/' 38 | encoding = 'utf8' 39 | regex = '([\d.]+)' 40 | 41 | [nvrs] 42 | source = "cratesio" 43 | cratesio = "nvrs" 44 | 45 | [libkeccak] 46 | source = "gitea" 47 | host = "codeberg.org" 48 | gitea = "maandree/libkeccak" 49 | use_max_tag = true 50 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | tab_spaces = 4 3 | reorder_imports = true 4 | reorder_modules = true 5 | -------------------------------------------------------------------------------- /src/api/aur.rs: -------------------------------------------------------------------------------- 1 | use crate::{api, error}; 2 | 3 | #[derive(serde::Deserialize)] 4 | struct AURResponse { 5 | results: Vec, 6 | } 7 | 8 | #[allow(non_snake_case)] 9 | #[derive(serde::Deserialize)] 10 | struct AURResult { 11 | Version: String, 12 | } 13 | 14 | /// get the latest version of a package from the AUR 15 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 16 | Box::pin(async move { 17 | let url = format!("https://aur.archlinux.org/rpc/v5/info/{}", args.args[0]); 18 | let client = args.request_client; 19 | 20 | let result = client.get(url).headers(api::setup_headers()).send().await?; 21 | api::match_statuscode(&result.status(), args.package.clone())?; 22 | 23 | let json: AURResponse = result.json().await?; 24 | 25 | if let Some(first) = json.results.first() { 26 | let version = first.Version.split_once('-').unwrap(); 27 | 28 | Ok(api::Release { 29 | name: version.0.to_string(), 30 | tag: None, 31 | url: String::new(), 32 | }) 33 | } else { 34 | Err(error::Error::NoVersion(args.package)) 35 | } 36 | }) 37 | } 38 | 39 | #[tokio::test] 40 | async fn request_test() { 41 | let package = "permitter".to_string(); 42 | let args = api::ApiArgs { 43 | request_client: reqwest::Client::new(), 44 | package: package.clone(), 45 | use_max_tag: None, 46 | args: vec![package], 47 | api_key: String::new(), 48 | }; 49 | 50 | assert!(get_latest(args).await.is_ok()); 51 | } 52 | -------------------------------------------------------------------------------- /src/api/crates_io.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Response; 2 | use serde_json::Value; 3 | 4 | use crate::{api, error}; 5 | 6 | /// get the latest version of a package from GitHub 7 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 8 | Box::pin(async move { 9 | let url = format!("https://crates.io/api/v1/crates/{}/versions", args.args[0]); 10 | 11 | let result = request(url, &args).await?; 12 | let json: Value = result.json().await?; 13 | let json = json.get("versions").unwrap(); 14 | 15 | for version in json.as_array().unwrap() { 16 | let ver = version.get("num").unwrap().to_string().replace("\"", ""); 17 | 18 | if ver.contains("-") { 19 | continue; 20 | } 21 | 22 | return Ok(api::Release { 23 | name: ver.clone(), 24 | tag: None, 25 | url: format!("https://crates.io/crates/{}/{}", args.args[0], ver), 26 | }); 27 | } 28 | 29 | Err(error::Error::NoVersion(args.args[0].clone())) 30 | }) 31 | } 32 | 33 | async fn request(url: String, args: &api::ApiArgs) -> error::Result { 34 | let headers = api::setup_headers(); 35 | let client = &args.request_client; 36 | 37 | let result = client.get(url).headers(headers).send().await?; 38 | api::match_statuscode(&result.status(), args.package.clone())?; 39 | 40 | Ok(result) 41 | } 42 | 43 | #[tokio::test] 44 | async fn request_test() { 45 | let package = "nvrs".to_string(); 46 | let args = api::ApiArgs { 47 | request_client: reqwest::Client::new(), 48 | package: package.clone(), 49 | use_max_tag: None, 50 | args: vec![package], 51 | api_key: String::new(), 52 | }; 53 | 54 | assert!(get_latest(args).await.is_ok()); 55 | } 56 | -------------------------------------------------------------------------------- /src/api/gitea.rs: -------------------------------------------------------------------------------- 1 | use crate::{api, error}; 2 | use reqwest::{Response, header::HeaderValue}; 3 | 4 | #[derive(serde::Deserialize)] 5 | #[serde(transparent)] 6 | struct GiteaTagResponse { 7 | tags: Vec, 8 | } 9 | 10 | #[derive(serde::Deserialize)] 11 | struct GiteaTag { 12 | name: String, 13 | } 14 | 15 | #[derive(serde::Deserialize)] 16 | #[serde(transparent)] 17 | struct GiteaReleaseResponse { 18 | releases: Vec, 19 | } 20 | 21 | #[derive(serde::Deserialize)] 22 | struct GiteaRelease { 23 | tag_name: String, 24 | html_url: String, 25 | } 26 | 27 | /// get the latest version of a package from Gitea 28 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 29 | Box::pin(async move { 30 | let host = if !args.args[1].is_empty() { 31 | &args.args[1] 32 | } else { 33 | "gitea.com" 34 | }; 35 | let repo_url = format!("https://{}/api/v1/repos/{}", host, args.args[0]); 36 | 37 | if args.use_max_tag.is_some_and(|x| x) { 38 | let url = format!("{}/tags", repo_url); 39 | 40 | let result = request(url, &args).await?; 41 | let json: &GiteaTag = &result.json::().await?.tags[0]; 42 | 43 | Ok(api::Release { 44 | name: json.name.clone(), 45 | tag: Some(json.name.clone()), 46 | url: format!("{}/releases/tag/{}", repo_url, json.name), 47 | }) 48 | } else { 49 | let url = format!("{}/releases", repo_url); 50 | let result = request(url, &args).await?; 51 | let json: &GiteaRelease = &result.json::().await?.releases[0]; 52 | 53 | let tag = json.tag_name.to_owned(); 54 | 55 | Ok(api::Release { 56 | name: tag.clone(), 57 | tag: Some(tag), 58 | url: json.html_url.clone(), 59 | }) 60 | } 61 | }) 62 | } 63 | 64 | async fn request(url: String, args: &api::ApiArgs) -> error::Result { 65 | let mut headers = api::setup_headers(); 66 | if !args.api_key.is_empty() { 67 | headers.insert( 68 | "PRIVATE-TOKEN", 69 | HeaderValue::from_str(&args.api_key).unwrap(), 70 | ); 71 | }; 72 | let client = &args.request_client; 73 | 74 | let result = client.get(url).headers(headers).send().await?; 75 | api::match_statuscode(&result.status(), args.package.clone())?; 76 | 77 | Ok(result) 78 | } 79 | 80 | #[tokio::test] 81 | async fn request_test() { 82 | let package = "maandree/libkeccak".to_string(); 83 | let args = api::ApiArgs { 84 | request_client: reqwest::Client::new(), 85 | package: package.clone(), 86 | use_max_tag: Some(true), 87 | args: vec![package, "codeberg.org".to_string()], 88 | api_key: String::new(), 89 | }; 90 | 91 | assert!(get_latest(args).await.is_ok()); 92 | } 93 | -------------------------------------------------------------------------------- /src/api/github.rs: -------------------------------------------------------------------------------- 1 | use reqwest::{ 2 | Response, 3 | header::{ACCEPT, AUTHORIZATION, HeaderValue}, 4 | }; 5 | use serde_json::Value; 6 | 7 | use crate::{api, error}; 8 | 9 | #[derive(serde::Deserialize)] 10 | struct GitHubResponse { 11 | tag_name: String, 12 | html_url: String, 13 | } 14 | 15 | /// get the latest version of a package from GitHub 16 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 17 | Box::pin(async move { 18 | let repo_url = format!("https://api.github.com/repos/{}", args.args[0]); 19 | 20 | if args.use_max_tag.is_some_and(|x| x) { 21 | let url = format!("{}/tags", repo_url); 22 | let result = request(url, &args).await?; 23 | let json: Value = result.json().await?; 24 | 25 | let max_tag = json 26 | .get(0) 27 | .unwrap() 28 | .get("name") 29 | .unwrap() 30 | .to_string() 31 | .replace("\"", ""); 32 | 33 | Ok(api::Release { 34 | name: max_tag.clone(), 35 | tag: Some(max_tag.clone()), 36 | url: format!( 37 | "https://github.com/{}/releases/tag/{}", 38 | args.args[0], max_tag 39 | ), 40 | }) 41 | } else { 42 | let url = format!("{}/releases/latest", repo_url); 43 | let result = request(url, &args).await?; 44 | let json: GitHubResponse = result.json().await?; 45 | 46 | Ok(api::Release { 47 | name: json.tag_name.clone(), 48 | tag: Some(json.tag_name), 49 | url: json.html_url, 50 | }) 51 | } 52 | }) 53 | } 54 | 55 | async fn request(url: String, args: &api::ApiArgs) -> error::Result { 56 | let mut headers = api::setup_headers(); 57 | headers.insert( 58 | ACCEPT, 59 | HeaderValue::from_static("application/vnd.github+json"), 60 | ); 61 | headers.insert( 62 | "X-GitHub-Api-Version", 63 | HeaderValue::from_static("2022-11-28"), 64 | ); 65 | if !args.api_key.is_empty() { 66 | let bearer = format!("Bearer {}", args.api_key); 67 | headers.insert(AUTHORIZATION, HeaderValue::from_str(&bearer).unwrap()); 68 | } 69 | let client = &args.request_client; 70 | 71 | let result = client.get(url).headers(headers).send().await?; 72 | api::match_statuscode(&result.status(), args.package.clone())?; 73 | 74 | Ok(result) 75 | } 76 | 77 | #[tokio::test] 78 | async fn request_test() { 79 | let package = "nvrs".to_string(); 80 | let args = api::ApiArgs { 81 | request_client: reqwest::Client::new(), 82 | package: package.clone(), 83 | use_max_tag: None, 84 | args: vec![format!("adamperkowski/{}", package)], 85 | api_key: String::new(), 86 | }; 87 | 88 | assert!(get_latest(args).await.is_ok()); 89 | } 90 | -------------------------------------------------------------------------------- /src/api/gitlab.rs: -------------------------------------------------------------------------------- 1 | use crate::{api, error}; 2 | use reqwest::{Response, header::HeaderValue}; 3 | use serde_json::Value; 4 | 5 | #[derive(serde::Deserialize)] 6 | struct GitLabResponse { 7 | tag_name: String, 8 | tag_path: String, 9 | } 10 | 11 | /// get the latest version of a package from GitLab 12 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 13 | Box::pin(async move { 14 | let host = if !args.args[1].is_empty() { 15 | &args.args[1] 16 | } else { 17 | "gitlab.com" 18 | }; 19 | let repo_url = format!( 20 | "https://{}/api/v4/projects/{}", 21 | host, 22 | args.args[0].replace("/", "%2F") 23 | ); 24 | 25 | if args.use_max_tag.is_some_and(|x| x) { 26 | let url = format!("{}/repository/tags", repo_url); 27 | let result = request(url, &args).await?; 28 | let json: Value = result.json().await?; 29 | 30 | let max_tag = json 31 | .get(0) 32 | .unwrap() 33 | .get("name") 34 | .unwrap() 35 | .to_string() 36 | .replace("\"", ""); 37 | 38 | Ok(api::Release { 39 | name: max_tag.clone(), 40 | tag: Some(max_tag.clone()), 41 | url: format!("https://{}/{}/-/tags/{}", host, args.args[0], max_tag), 42 | }) 43 | } else { 44 | let url = format!("{}/releases/permalink/latest", repo_url); 45 | let result = request(url, &args).await?; 46 | let json: GitLabResponse = result.json().await?; 47 | 48 | Ok(api::Release { 49 | name: json.tag_name.clone(), 50 | tag: Some(json.tag_name), 51 | url: format!("https://{}{}", host, json.tag_path), 52 | }) 53 | } 54 | }) 55 | } 56 | 57 | async fn request(url: String, args: &api::ApiArgs) -> error::Result { 58 | let mut headers = api::setup_headers(); 59 | if !args.api_key.is_empty() { 60 | headers.insert( 61 | "PRIVATE-TOKEN", 62 | HeaderValue::from_str(&args.api_key).unwrap(), 63 | ); 64 | }; 65 | let client = &args.request_client; 66 | 67 | let result = client.get(url).headers(headers).send().await?; 68 | api::match_statuscode(&result.status(), args.package.clone())?; 69 | 70 | Ok(result) 71 | } 72 | 73 | #[tokio::test] 74 | async fn request_test() { 75 | let package = "mkinitcpio".to_string(); 76 | let args = api::ApiArgs { 77 | request_client: reqwest::Client::new(), 78 | package: package.clone(), 79 | use_max_tag: None, 80 | args: vec![ 81 | format!("archlinux/{0}/{0}", package), 82 | "gitlab.archlinux.org".to_string(), 83 | ], 84 | api_key: String::new(), 85 | }; 86 | 87 | assert!(get_latest(args).await.is_ok()); 88 | } 89 | -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | //! this module handles management & communication with sources, also knows as APIs 2 | 3 | #[cfg(feature = "aur")] 4 | mod aur; 5 | #[cfg(feature = "crates-io")] 6 | mod crates_io; 7 | #[cfg(feature = "gitea")] 8 | mod gitea; 9 | #[cfg(feature = "github")] 10 | mod github; 11 | #[cfg(feature = "gitlab")] 12 | mod gitlab; 13 | #[cfg(feature = "regex")] 14 | mod regex; 15 | 16 | /// struct containing the API name & a pointer to API's `get_latest` function 17 | pub struct Api { 18 | /// name of the API 19 | pub name: &'static str, 20 | /// pointer to the API's `get_latest` function 21 | pub(crate) func: fn(ApiArgs) -> ReleaseFuture, 22 | } 23 | 24 | /// arguments passed to a source 25 | pub(crate) struct ApiArgs { 26 | pub request_client: reqwest::Client, 27 | /// name of the package 28 | pub package: String, 29 | pub use_max_tag: Option, 30 | /// arguments passed to the source 31 | pub args: Vec, 32 | pub api_key: String, // empty String if none 33 | } 34 | 35 | /// this is what `get_latest`s return 36 | #[derive(Debug)] 37 | pub struct Release { 38 | /// name of the package 39 | pub name: String, 40 | /// version of the package 41 | pub tag: Option, 42 | /// url to the version's source 43 | pub url: String, 44 | } 45 | 46 | // this is necessary because we need to store a reference to an async function in `Api` 47 | type ReleaseFuture = 48 | std::pin::Pin> + Send>>; 49 | 50 | fn setup_headers() -> reqwest::header::HeaderMap { 51 | use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT}; 52 | 53 | let mut headers = HeaderMap::new(); 54 | headers.insert(USER_AGENT, HeaderValue::from_static("nvrs")); 55 | 56 | headers 57 | } 58 | 59 | fn match_statuscode(status: &reqwest::StatusCode, package: String) -> crate::error::Result<()> { 60 | use crate::error; 61 | use reqwest::StatusCode; 62 | 63 | match status.to_owned() { 64 | StatusCode::OK => Ok(()), 65 | StatusCode::FORBIDDEN => Err(error::Error::RequestForbidden(package)), 66 | _ => Err(error::Error::RequestNotOK(package, status.to_string())), 67 | } 68 | } 69 | 70 | /// public list of available sources 71 | pub const API_LIST: &[Api] = &[ 72 | #[cfg(feature = "aur")] 73 | Api { 74 | name: "aur", 75 | func: aur::get_latest, 76 | }, 77 | #[cfg(feature = "crates-io")] 78 | Api { 79 | name: "cratesio", 80 | func: crates_io::get_latest, 81 | }, 82 | #[cfg(feature = "gitea")] 83 | Api { 84 | name: "gitea", 85 | func: gitea::get_latest, 86 | }, 87 | #[cfg(feature = "github")] 88 | Api { 89 | name: "github", 90 | func: github::get_latest, 91 | }, 92 | #[cfg(feature = "gitlab")] 93 | Api { 94 | name: "gitlab", 95 | func: gitlab::get_latest, 96 | }, 97 | #[cfg(feature = "regex")] 98 | Api { 99 | name: "regex", 100 | func: regex::get_latest, 101 | }, 102 | ]; 103 | 104 | #[test] 105 | fn statuscode_matching_test() { 106 | use reqwest::StatusCode; 107 | 108 | assert!(match_statuscode(&StatusCode::OK, String::new()).is_ok()); 109 | assert!(match_statuscode(&StatusCode::IM_A_TEAPOT, String::new()).is_err()); 110 | } 111 | -------------------------------------------------------------------------------- /src/api/regex.rs: -------------------------------------------------------------------------------- 1 | use crate::{api, error}; 2 | use regex::Regex; 3 | 4 | /// get a version string from a webpage 5 | pub fn get_latest(args: api::ApiArgs) -> api::ReleaseFuture { 6 | Box::pin(async move { 7 | let url = args.args[0].clone(); 8 | let client = args.request_client; 9 | 10 | let result = client 11 | .get(&url) 12 | .headers(api::setup_headers()) 13 | .send() 14 | .await?; 15 | api::match_statuscode(&result.status(), args.package.clone())?; 16 | 17 | let body = result.text().await?; 18 | 19 | let re = Regex::new(&args.args[1]).unwrap(); 20 | if let Some(caps) = re.captures(&body) { 21 | Ok(api::Release { 22 | name: caps.get(1).unwrap().as_str().to_owned(), 23 | tag: None, 24 | url, 25 | }) 26 | } else { 27 | Err(error::Error::NoVersion(args.package)) 28 | } 29 | }) 30 | } 31 | 32 | #[tokio::test] 33 | async fn request_test() { 34 | let package = "rustrover".to_string(); 35 | let args = api::ApiArgs { 36 | request_client: reqwest::Client::new(), 37 | package: package.clone(), 38 | use_max_tag: None, 39 | args: vec![ 40 | "https://data.services.jetbrains.com/products?code=RR&release.type=release".to_string(), 41 | r"RustRover-([\d.]+).tar.gz".to_string(), 42 | ], 43 | api_key: String::new(), 44 | }; 45 | 46 | assert!(crate::api::regex::get_latest(args).await.is_ok()); 47 | } 48 | -------------------------------------------------------------------------------- /src/cli/args.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | 4 | const COPYRIGHT_TEXT: &str = 5 | "Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the \"Software\"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions:\n 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software."; 13 | 14 | #[derive(Clone, Parser)] 15 | #[command(version, about)] 16 | pub struct Cli { 17 | #[arg( 18 | short = 'c', 19 | long, 20 | help = "Compare newver with oldver and display differences as updates" 21 | )] 22 | pub cmp: bool, 23 | 24 | #[arg( 25 | short = 't', 26 | long, 27 | value_name = "packages", 28 | help = "Comma-separated list of packages to update automatically (use `ALL` for all)", 29 | value_delimiter = ',' 30 | )] 31 | pub take: Option>, 32 | 33 | #[arg( 34 | short = 'n', 35 | long, 36 | value_name = "packages", 37 | help = "Comma-separated list of packages to delete from the config", 38 | value_delimiter = ',' 39 | )] 40 | pub nuke: Option>, 41 | 42 | #[arg(long, help = "Don't exit on recoverable errors")] 43 | pub no_fail: bool, 44 | 45 | #[arg(long, help = "List available sources")] 46 | pub list_sources: bool, 47 | 48 | #[arg( 49 | long = "config", 50 | value_name = "path", 51 | help = "Override path to the config file" 52 | )] 53 | pub custom_config: Option, 54 | 55 | #[arg(long, help = "Display copyright information")] 56 | copyright: bool, 57 | } 58 | 59 | pub fn get_args() -> Cli { 60 | let cli = Cli::parse(); 61 | 62 | if cli.copyright { 63 | let current_year = 1970 64 | + (SystemTime::now() 65 | .duration_since(UNIX_EPOCH) 66 | .expect("time went backwards") 67 | .as_secs() 68 | / (365 * 24 * 60 * 60)); 69 | 70 | println!( 71 | "Copyright (c) {} Adam Perkowski\n{}", 72 | current_year, COPYRIGHT_TEXT 73 | ); 74 | 75 | std::process::exit(0); 76 | } else if cli.list_sources { 77 | for api in nvrs::api::API_LIST { 78 | println!("{}", api.name); 79 | } 80 | 81 | std::process::exit(0); 82 | } 83 | 84 | cli 85 | } 86 | -------------------------------------------------------------------------------- /src/cli/main.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | 3 | use nvrs::*; 4 | 5 | mod args; 6 | 7 | #[tokio::main] 8 | async fn main() -> error::Result<()> { 9 | match init().await { 10 | Ok(core) => { 11 | let res = if core.1.cmp { 12 | compare(core.0).await 13 | } else if core.1.take.is_some() { 14 | take(core.0, core.1.take).await 15 | } else if core.1.nuke.is_some() { 16 | nuke(core.0, core.1.nuke, core.1.no_fail).await 17 | } else { 18 | sync(core.0, core.1.no_fail).await 19 | }; 20 | 21 | match res { 22 | Ok(_) => (), 23 | Err(e) => e.pretty(), 24 | } 25 | } 26 | Err(e) => e.pretty(), 27 | } 28 | 29 | Ok(()) 30 | } 31 | 32 | async fn init() -> error::Result<(Core, args::Cli)> { 33 | let cli = args::get_args(); 34 | let config = config::load(&cli.custom_config).await?; 35 | 36 | let verfiles = verfiles::load(&config.0.__config__).await?; 37 | let keyfile = keyfile::load(&config.0.__config__).await?; 38 | 39 | Ok(( 40 | Core { 41 | config, 42 | verfiles, 43 | client: reqwest::Client::new(), 44 | keyfile, 45 | }, 46 | cli, 47 | )) 48 | } 49 | 50 | async fn compare(core: Core) -> error::Result<()> { 51 | let (oldver, newver) = core.verfiles; 52 | 53 | for new_pkg in newver.data.data { 54 | if let Some(old_pkg) = oldver.data.data.iter().find(|p| p.0 == &new_pkg.0) { 55 | if old_pkg.1.version != new_pkg.1.version { 56 | println!( 57 | "{} {} {} -> {}", 58 | "*".white().on_black(), 59 | new_pkg.0.blue(), 60 | old_pkg.1.version.red(), 61 | new_pkg.1.version.green() 62 | ); 63 | } 64 | } else { 65 | println!( 66 | "{} {} {} -> {}", 67 | "*".white().on_black(), 68 | new_pkg.0.blue(), 69 | "NONE".red(), 70 | new_pkg.1.version.green() 71 | ); 72 | } 73 | } 74 | 75 | Ok(()) 76 | } 77 | 78 | async fn take(core: Core, take_names: Option>) -> error::Result<()> { 79 | let names = take_names.unwrap(); 80 | let config = core.config; 81 | let (mut oldver, newver) = core.verfiles; 82 | 83 | let packages_to_update = if names.contains(&"ALL".to_string()) { 84 | newver.data.data.keys().cloned().collect() 85 | } else { 86 | names 87 | }; 88 | 89 | for package_name in packages_to_update { 90 | if let Some(new_pkg) = newver.data.data.get(&package_name) { 91 | if let Some(old_pkg) = oldver.data.data.get_mut(&package_name) { 92 | if old_pkg.version != new_pkg.version { 93 | println!( 94 | "{} {} {} -> {}", 95 | "+".white().on_black(), 96 | package_name.blue(), 97 | old_pkg.version.red(), 98 | new_pkg.version.green() 99 | ); 100 | 101 | let pkg = new_pkg.to_owned(); 102 | old_pkg.version = pkg.version; 103 | old_pkg.gitref = pkg.gitref; 104 | old_pkg.url = pkg.url; 105 | } 106 | } else { 107 | println!( 108 | "{} {} {} -> {}", 109 | "+".white().on_black(), 110 | package_name.blue(), 111 | "NONE".red(), 112 | new_pkg.version.green() 113 | ); 114 | oldver.data.data.insert(package_name, new_pkg.to_owned()); 115 | } 116 | } else { 117 | return Err(error::Error::PkgNotInNewver(package_name)); 118 | } 119 | } 120 | 121 | verfiles::save(&oldver, true, &config.0.__config__).await 122 | } 123 | 124 | async fn nuke(core: Core, nuke_names: Option>, no_fail: bool) -> error::Result<()> { 125 | let names = nuke_names.unwrap(); 126 | let mut config_content = core.config.0; 127 | let (mut oldver, mut newver) = core.verfiles; 128 | 129 | for package_name in names { 130 | if config_content.packages.contains_key(&package_name) { 131 | config_content.packages.remove(&package_name); 132 | } else if no_fail { 133 | error::Error::PkgNotInConfig(package_name.clone()).pretty(); 134 | } else { 135 | return Err(error::Error::PkgNotInConfig(package_name)); 136 | } 137 | newver.data.data.remove(&package_name); 138 | oldver.data.data.remove(&package_name); 139 | } 140 | 141 | verfiles::save(&newver, false, &config_content.__config__).await?; 142 | verfiles::save(&oldver, true, &config_content.__config__).await?; 143 | config::save(&config_content, core.config.1).await?; 144 | 145 | Ok(()) 146 | } 147 | 148 | async fn sync(core: Core, no_fail: bool) -> error::Result<()> { 149 | let config = core.config.0; 150 | let (_, mut newver) = core.verfiles; 151 | 152 | let tasks: Vec<_> = config 153 | .packages 154 | .clone() 155 | .into_iter() 156 | .map(|pkg| tokio::spawn(run_source(pkg, core.client.clone(), core.keyfile.clone()))) 157 | .collect(); 158 | 159 | let mut results = futures::future::join_all(tasks).await; 160 | 161 | for package in config.packages { 162 | match results.remove(0).unwrap() { 163 | Ok(release) => { 164 | let gitref: String; 165 | let tag = if let Some(t) = release.tag.clone() { 166 | gitref = format!("refs/tags/{}", t); 167 | release.tag.unwrap().replacen(&package.1.prefix, "", 1) 168 | } else { 169 | gitref = String::new(); 170 | release.name 171 | }; 172 | 173 | if let Some(new_pkg) = newver.data.data.iter_mut().find(|p| p.0 == &package.0) { 174 | if new_pkg.1.version != tag { 175 | println!( 176 | "{} {} {} -> {}", 177 | "|".white().on_black(), 178 | package.0.blue(), 179 | new_pkg.1.version.red(), 180 | tag.green() 181 | ); 182 | 183 | new_pkg.1.version = tag; 184 | new_pkg.1.gitref = gitref; 185 | new_pkg.1.url = release.url; 186 | } 187 | } else { 188 | println!( 189 | "{} {} {} -> {}", 190 | "|".white().on_black(), 191 | package.0.blue(), 192 | "NONE".red(), 193 | tag.green() 194 | ); 195 | 196 | newver.data.data.insert( 197 | package.0, 198 | verfiles::VerPackage { 199 | version: tag, 200 | gitref, 201 | url: release.url, 202 | }, 203 | ); 204 | } 205 | } 206 | Err(e) => { 207 | if !no_fail { 208 | return Err(e); 209 | } else { 210 | e.pretty(); 211 | } 212 | } 213 | }; 214 | } 215 | 216 | verfiles::save(&newver, false, &config.__config__).await 217 | } 218 | 219 | #[tokio::test] 220 | async fn core_initializing() { 221 | assert!(init().await.is_ok()) 222 | } 223 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | //! operations on configuration files 2 | //! 3 | //! see the [example `nvrs.toml`](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml) 4 | 5 | use crate::error; 6 | use serde::{Deserialize, Serialize}; 7 | use std::{ 8 | collections::BTreeMap, 9 | env, 10 | path::{Path, PathBuf}, 11 | }; 12 | use tokio::{fs, io::AsyncWriteExt}; 13 | 14 | /// main configuration file structure 15 | /// 16 | /// see the [example `nvrs.toml`](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml) 17 | #[derive(Debug, Clone, Deserialize, Serialize)] 18 | pub struct Config { 19 | /// `__config__` table 20 | pub __config__: Option, 21 | /// list of custom package tables 22 | #[serde(flatten)] 23 | pub packages: BTreeMap, 24 | } 25 | 26 | /// `__config__` table structure 27 | /// 28 | /// see the [example `nvrs.toml`](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml) 29 | #[derive(Debug, Clone, Deserialize, Serialize)] 30 | pub struct ConfigTable { 31 | /// path to the `oldver` file 32 | pub oldver: Option, 33 | /// path to the `newver` file 34 | pub newver: Option, 35 | /// path to the keyfile 36 | pub(crate) keyfile: Option, 37 | } 38 | 39 | /// package entry structure 40 | /// 41 | /// see the [example `nvrs.toml`](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml) 42 | #[derive(Debug, Clone, Deserialize, Serialize)] 43 | pub struct Package { 44 | source: String, // ex. "github", "aur" 45 | #[serde(default)] 46 | #[serde(skip_serializing_if = "is_empty_string")] 47 | host: String, // ex. "gitlab.archlinux.org" 48 | 49 | // equivalent to `target` in api::ApiArgs 50 | #[cfg(feature = "aur")] 51 | #[serde(default)] 52 | #[serde(skip_serializing_if = "is_empty_string")] 53 | aur: String, 54 | #[cfg(feature = "crates-io")] 55 | #[serde(default)] 56 | #[serde(skip_serializing_if = "is_empty_string")] 57 | cratesio: String, 58 | #[cfg(feature = "gitea")] 59 | #[serde(default)] 60 | #[serde(skip_serializing_if = "is_empty_string")] 61 | gitea: String, 62 | #[cfg(feature = "github")] 63 | #[serde(default)] 64 | #[serde(skip_serializing_if = "is_empty_string")] 65 | github: String, 66 | #[cfg(feature = "gitlab")] 67 | #[serde(default)] 68 | #[serde(skip_serializing_if = "is_empty_string")] 69 | gitlab: String, 70 | #[cfg(feature = "regex")] 71 | #[serde(default)] 72 | url: String, 73 | #[cfg(feature = "regex")] 74 | #[serde(default)] 75 | regex: String, 76 | 77 | /// whether to use the latest tag instead of the latest release 78 | #[serde(default)] 79 | pub(crate) use_max_tag: Option, 80 | /// prefix to add to the version name 81 | #[serde(default)] 82 | #[serde(skip_serializing_if = "is_empty_string")] 83 | pub prefix: String, 84 | } 85 | 86 | impl Package { 87 | /// manually create a new package entry 88 | pub fn new( 89 | source: String, 90 | target: String, 91 | use_max_tag: bool, 92 | prefix: String, 93 | ) -> error::Result { 94 | let mut package = Package::default(); 95 | 96 | match source.as_str() { 97 | #[cfg(feature = "aur")] 98 | "aur" => { 99 | package.aur = target; 100 | Ok(()) 101 | } 102 | #[cfg(feature = "crates-io")] 103 | "cratesio" => { 104 | package.cratesio = target; 105 | Ok(()) 106 | } 107 | #[cfg(feature = "gitea")] 108 | "gitea" => { 109 | package.gitea = target; 110 | Ok(()) 111 | } 112 | #[cfg(feature = "github")] 113 | "github" => { 114 | package.github = target; 115 | Ok(()) 116 | } 117 | #[cfg(feature = "gitlab")] 118 | "gitlab" => { 119 | package.gitlab = target; 120 | Ok(()) 121 | } 122 | #[cfg(feature = "regex")] 123 | "regex" => { 124 | package.url = target; 125 | Ok(()) 126 | } 127 | _ => Err(error::Error::SourceNotFound(source.clone())), 128 | }?; 129 | 130 | package.source = source; 131 | package.use_max_tag = Some(use_max_tag); 132 | package.prefix = prefix; 133 | 134 | Ok(package) 135 | } 136 | 137 | fn default() -> Self { 138 | Package { 139 | source: String::new(), 140 | host: String::new(), 141 | #[cfg(feature = "aur")] 142 | aur: String::new(), 143 | #[cfg(feature = "crates-io")] 144 | cratesio: String::new(), 145 | #[cfg(feature = "gitea")] 146 | gitea: String::new(), 147 | #[cfg(feature = "github")] 148 | github: String::new(), 149 | #[cfg(feature = "gitlab")] 150 | gitlab: String::new(), 151 | #[cfg(feature = "regex")] 152 | url: String::new(), 153 | #[cfg(feature = "regex")] 154 | regex: String::new(), 155 | use_max_tag: None, 156 | prefix: String::new(), 157 | } 158 | } 159 | 160 | /// global function to get various API-specific agrs for a package 161 | /// 162 | /// # example 163 | /// ```rust 164 | /// use nvrs::config::Package; 165 | /// 166 | /// let package = Package::new("github".to_string(), "adamperkowski/nvrs".to_string(), 167 | /// false, "v".to_string()).unwrap(); 168 | /// 169 | /// let args = package.get_api(); 170 | /// 171 | /// assert_eq!(args, ("github".to_string(), vec!["adamperkowski/nvrs".to_string()])) 172 | /// ``` 173 | pub fn get_api(&self) -> (String, Vec) { 174 | let self_ref = self.to_owned(); 175 | let args = match self.source.as_str() { 176 | #[cfg(feature = "aur")] 177 | "aur" => vec![self_ref.aur], 178 | #[cfg(feature = "crates-io")] 179 | "cratesio" => vec![self_ref.cratesio], 180 | #[cfg(feature = "gitea")] 181 | "gitea" => vec![self_ref.gitea, self_ref.host], 182 | #[cfg(feature = "github")] 183 | "github" => vec![self_ref.github], 184 | #[cfg(feature = "gitlab")] 185 | "gitlab" => vec![self_ref.gitlab, self_ref.host], 186 | #[cfg(feature = "regex")] 187 | "regex" => vec![self_ref.url, self_ref.regex], 188 | _ => vec![], 189 | }; 190 | 191 | (self_ref.source, args) 192 | } 193 | } 194 | 195 | /// global asynchronous function to load all config files 196 | pub async fn load(custom_path: &Option) -> error::Result<(Config, PathBuf)> { 197 | let config_path = if let Some(path) = custom_path { 198 | let path = Path::new(&path); 199 | if path.exists() && path.is_file() { 200 | path.to_path_buf() 201 | } else { 202 | return Err(error::Error::NoConfigSpecified); 203 | } 204 | } else { 205 | let default_path = Path::new("nvrs.toml"); 206 | let config_home = format!( 207 | "{}/nvrs/nvrs.toml", 208 | env::var("XDG_CONFIG_HOME").unwrap_or(expand_tilde("~/.config".to_string())?) 209 | ); 210 | let config_home_non_xdg = expand_tilde("~/.config/nvrs.toml".to_string())?; 211 | let config_home_non_xdg = Path::new(&config_home_non_xdg); 212 | let home_path = Path::new(&config_home); 213 | 214 | if default_path.exists() && default_path.is_file() { 215 | default_path.to_path_buf() 216 | } else if home_path.exists() && home_path.is_file() { 217 | home_path.to_path_buf() 218 | } else if config_home_non_xdg.exists() && config_home_non_xdg.is_file() { 219 | config_home_non_xdg.to_path_buf() 220 | } else { 221 | return Err(error::Error::NoConfig); 222 | } 223 | }; 224 | 225 | let content = fs::read_to_string(&config_path).await?; 226 | let toml_content: Config = toml::from_str(&content)?; 227 | 228 | Ok((toml_content, config_path)) 229 | } 230 | 231 | // FIXME: this nukes all the comments 232 | /// global asynchronous function to save the config file 233 | pub async fn save(config_content: &Config, path: PathBuf) -> error::Result<()> { 234 | let mut file = fs::File::create(path).await?; 235 | let content = format!("{}\n", toml::to_string(&config_content)?); 236 | file.write_all(content.as_bytes()).await?; 237 | file.shutdown().await?; 238 | 239 | Ok(()) 240 | } 241 | 242 | pub(crate) fn expand_tilde(s: String) -> error::Result { 243 | if let Some(stripped) = s.strip_prefix('~') { 244 | let home = env::var("HOME")?; 245 | return Ok(home + stripped); 246 | } 247 | Ok(s) 248 | } 249 | 250 | fn is_empty_string(s: &str) -> bool { 251 | s.is_empty() 252 | } 253 | 254 | #[cfg(test)] 255 | mod tests { 256 | use super::*; 257 | 258 | #[tokio::test] 259 | async fn loading() { 260 | let config = load(&None).await.unwrap(); 261 | 262 | assert_eq!(config.1, PathBuf::from("nvrs.toml")); 263 | } 264 | 265 | #[tokio::test] 266 | async fn manual_package() { 267 | assert!( 268 | Package::new( 269 | "non_existing_source".to_string(), 270 | "non_existing".to_string(), 271 | false, 272 | String::new() 273 | ) 274 | .is_err() 275 | ); 276 | assert!( 277 | Package::new( 278 | "github".to_string(), 279 | "orhun/git-cliff".to_string(), 280 | false, 281 | "v".to_string() 282 | ) 283 | .is_ok() 284 | ); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! [thiserror] implementation 2 | 3 | use thiserror::Error as ThisError; 4 | 5 | #[cfg(feature = "colored")] 6 | use colored::Colorize; 7 | 8 | const RATE_LIMIT: &str = "we might be getting rate-limited here"; 9 | const CONFIG_PATHS: &str = "config file locations: 10 | ./nvrs.toml 11 | $XDG_CONFIG_HOME/nvrs/nvrs.toml 12 | $HOME/.config/nvrs.toml"; 13 | const NOT_EMPTY: &str = "make sure the file is not empty"; 14 | const EXAMPLE_CONFIG_TABLE: &str = "example: 15 | [__config__] 16 | oldver = \"oldver.json\" 17 | newver = \"newver.json\""; 18 | 19 | /// custom Error type for nvrs 20 | #[derive(Debug, ThisError)] 21 | pub enum Error { 22 | /// [reqwest] errors 23 | #[error("request error: {0}")] 24 | RequestError(#[from] reqwest::Error), 25 | 26 | /// [std::io] errors 27 | #[error("io error: {0}")] 28 | IOError(#[from] std::io::Error), 29 | 30 | /// [serde_json] errors 31 | #[error("json parsing error: {0}")] 32 | JSONError(#[from] serde_json::Error), 33 | 34 | /// [toml::de] errors 35 | #[error("toml parsing error: {0}")] 36 | TOMLError(#[from] toml::de::Error), 37 | 38 | /// [toml::ser] errors 39 | #[error("toml parsing error: {0}")] 40 | TOMLErrorSer(#[from] toml::ser::Error), 41 | 42 | /// [std::env] errors 43 | #[error("env error: {0}")] 44 | EnvError(#[from] std::env::VarError), 45 | 46 | // custom errors 47 | /// request status != OK 48 | #[error("{0}: request status != OK\n{1}")] 49 | RequestNotOK(String, String), 50 | 51 | /// request status == 430 52 | #[error("{0}: request returned 430\n{RATE_LIMIT}")] 53 | RequestForbidden(String), 54 | 55 | /// latest version of a package not found 56 | #[error("{0}: version not found")] 57 | NoVersion(String), 58 | 59 | /// specified configuration file not found 60 | #[error("specified config file not found")] 61 | NoConfigSpecified, 62 | 63 | /// configuration file not found in any of the default locations 64 | #[error("no config found\n{CONFIG_PATHS}\n{NOT_EMPTY}")] 65 | NoConfig, 66 | 67 | /// no `__config__` in the configuration file 68 | #[error("__config__ not specified\n{EXAMPLE_CONFIG_TABLE}")] 69 | NoConfigTable, 70 | 71 | /// keyfile specified in the configuration not found 72 | #[error("specified keyfile not found\n{NOT_EMPTY}")] 73 | NoKeyfile, 74 | 75 | /// no `oldver` or `newver` in `__config__` 76 | #[error("oldver & newver not specified\n{EXAMPLE_CONFIG_TABLE}")] 77 | NoXVer, 78 | 79 | /// unsupported verfile version 80 | #[error("unsupported verfile version\nplease update your verfiles")] 81 | VerfileVer, 82 | 83 | /// package not found in newver 84 | #[error("{0}: package not in newver")] 85 | PkgNotInNewver(String), 86 | 87 | /// package not found in config 88 | #[error("{0}: package not in config")] 89 | PkgNotInConfig(String), 90 | 91 | /// source / API not found 92 | #[error("source {0} not found")] 93 | SourceNotFound(String), 94 | } 95 | 96 | impl Error { 97 | /// display a pretty formatted error message 98 | /// # example usage 99 | /// ```rust 100 | /// use nvrs::error; 101 | /// 102 | /// let config_err = error::Error::NoConfig; 103 | /// let source_err = error::Error::SourceNotFound("github".to_string()); 104 | /// 105 | ///println!("config error:\n"); 106 | /// config_err.pretty(); 107 | ///println!("\n\nsource error:\n"); 108 | /// source_err.pretty(); 109 | /// ``` 110 | /// the above example will result in: 111 | /// [image](https://imgur.com/a/4SZeFXn) 112 | #[cfg(feature = "colored")] 113 | pub fn pretty(&self) { 114 | let mut lines: Vec = self 115 | .to_string() 116 | .lines() 117 | .map(|line| line.to_string()) 118 | .collect(); 119 | let first = lines.remove(0); 120 | let first_split = first.split_once(':').unwrap_or(("", &first)); 121 | if first_split.0.is_empty() { 122 | println!("{} {}", "!".red().bold().on_black(), first_split.1.red()); 123 | } else { 124 | println!( 125 | "{} {}:{}", 126 | "!".red().bold().on_black(), 127 | first_split.0, 128 | first_split.1.red() 129 | ); 130 | } 131 | for line in lines { 132 | println!("{} {}", "!".red().on_black(), line) 133 | } 134 | } 135 | } 136 | 137 | /// custom Result type for nvrs 138 | pub type Result = std::result::Result; 139 | 140 | #[test] 141 | fn test_error() { 142 | let message = "nvrs died. now why could that be...?"; 143 | let error = Error::from(std::io::Error::other(message)); 144 | assert_eq!( 145 | format!("\"io error: {message}\""), 146 | format!("{:?}", error.to_string()) 147 | ) 148 | } 149 | -------------------------------------------------------------------------------- /src/keyfile.rs: -------------------------------------------------------------------------------- 1 | //! operations on keyfiles 2 | //! 3 | //! see the [example `nvrs.toml`](https://github.com/adamperkowski/nvrs/blob/main/nvrs.toml) & [example `keyfile.toml`](https://github.com/adamperkowski/nvrs/blob/main/n_keyfile.toml) 4 | 5 | use crate::{config, error}; 6 | use serde::Deserialize; 7 | use std::path::Path; 8 | use tokio::fs; 9 | 10 | /// keyfile structure 11 | /// 12 | /// see `keyfile` in [crate::config::ConfigTable] 13 | #[derive(Clone, Deserialize)] 14 | pub struct Keyfile { 15 | keys: KeysTable, 16 | } 17 | 18 | /// `[keys]` table structure 19 | /// 20 | /// see the [example `keyfile.toml`](https://github.com/adamperkowski/nvrs/blob/main/n_keyfile.toml) 21 | #[derive(Clone, Deserialize)] 22 | struct KeysTable { 23 | #[cfg(feature = "github")] 24 | #[serde(default)] 25 | #[serde(skip_serializing_if = "config::is_empty_string")] 26 | github: String, 27 | #[cfg(feature = "gitlab")] 28 | #[serde(default)] 29 | #[serde(skip_serializing_if = "config::is_empty_string")] 30 | gitlab: String, 31 | } 32 | 33 | impl Keyfile { 34 | /// returns API key for the specified API name (empty string if not found) 35 | pub async fn get_key(&self, api_name: &str) -> String { 36 | let keys = self.keys.to_owned(); 37 | match api_name { 38 | #[cfg(feature = "github")] 39 | "github" => keys.github, 40 | #[cfg(feature = "gitlab")] 41 | "gitlab" => keys.gitlab, 42 | _ => String::new(), 43 | } 44 | } 45 | } 46 | 47 | /// load contents of the specified keyfile 48 | /// 49 | /// see `keyfile` in [crate::config::ConfigTable] 50 | pub async fn load(config_content: &Option) -> error::Result> { 51 | if let Some(config_table) = config_content { 52 | if let Some(keyfile) = config_table.to_owned().keyfile { 53 | let keyfile = config::expand_tilde(keyfile)?; 54 | let keyfile_path = Path::new(&keyfile); 55 | let keyfile_content = if keyfile_path.exists() && keyfile_path.is_file() { 56 | fs::read_to_string(keyfile_path).await? 57 | } else { 58 | String::new() 59 | }; 60 | 61 | if keyfile_content.is_empty() { 62 | return Err(error::Error::NoKeyfile); 63 | } 64 | 65 | Ok(Some(toml::from_str(&keyfile_content)?)) 66 | } else { 67 | Ok(None) 68 | } 69 | } else { 70 | Ok(None) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! nvrs - fast new version checker for software releases 🚦🦀 2 | //! 3 | //!
4 | //! 5 | //! nvrs is still a WIP 6 | //! 7 | //! new features & bugfixes are being pushed every day 8 | //! 9 | //! you may encounter some issues. please consider [submitting feedback](https://github.com/adamperkowski/nvrs/issues/new/choose) if you do. 10 | //! 11 | //!
12 | 13 | pub mod api; 14 | pub mod config; 15 | pub mod error; 16 | pub mod keyfile; 17 | pub mod verfiles; 18 | 19 | /// example "core" struct that holds all the necessary data for the library 20 | /// 21 | /// # example usage 22 | /// ```rust 23 | /// # tokio_test::block_on(async { 24 | /// use nvrs::*; 25 | /// 26 | /// let config = config::load(&None).await.unwrap(); 27 | /// let verfiles = verfiles::load(&config.0.__config__).await.unwrap(); 28 | /// let keyfile = keyfile::load(&config.0.__config__).await.unwrap(); 29 | /// 30 | /// Core { 31 | /// config, 32 | /// verfiles, 33 | /// client: reqwest::Client::new(), 34 | /// keyfile, 35 | /// }; 36 | /// # }) 37 | /// ``` 38 | pub struct Core { 39 | /// configuration file content and path 40 | pub config: (config::Config, std::path::PathBuf), 41 | /// verfiles contents 42 | pub verfiles: (verfiles::Verfile, verfiles::Verfile), 43 | /// reqwest HTTP client 44 | pub client: reqwest::Client, 45 | /// keyfile contents 46 | pub keyfile: Option, 47 | } 48 | 49 | /// an asynchronous function that package's source and gets the latest release 50 | /// # example usage 51 | /// ```rust 52 | /// # tokio_test::block_on(async { 53 | /// use nvrs::{run_source, config}; 54 | /// 55 | /// let package_name = "nvrs".to_string(); 56 | /// let package = config::Package::new("github".to_string(), "adamperkowski/nvrs".to_string(), false, "v".to_string()).unwrap(); 57 | /// 58 | /// let client = reqwest::Client::new(); 59 | /// 60 | /// run_source((package_name, package), client, None).await; 61 | /// # }) 62 | /// ``` 63 | /// see [crate::config::Package] for `package` 64 | pub async fn run_source( 65 | package: (String, config::Package), 66 | client: reqwest::Client, 67 | keyfile: Option, 68 | ) -> error::Result { 69 | let (source, api_args) = package.1.get_api(); 70 | 71 | if let Some(api) = api::API_LIST.iter().find(|a| a.name == source) { 72 | let api_key = if let Some(keyfile_content) = keyfile { 73 | keyfile_content.get_key(api.name).await 74 | } else { 75 | String::new() 76 | }; 77 | 78 | let args = api::ApiArgs { 79 | request_client: client, 80 | package: package.0, 81 | use_max_tag: package.1.use_max_tag, 82 | args: api_args, 83 | api_key, 84 | }; 85 | 86 | Ok((api.func)(args).await?) 87 | } else { 88 | Err(error::Error::SourceNotFound(source.to_string())) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/tui/main.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/verfiles.rs: -------------------------------------------------------------------------------- 1 | //! operations on version files 2 | //! 3 | //! see `newver` & `oldver` in [crate::config::ConfigTable] 4 | 5 | use crate::{config, error}; 6 | use serde::{Deserialize, Serialize}; 7 | use std::{collections::BTreeMap, path::Path}; 8 | use tokio::{fs, io::AsyncWriteExt}; 9 | 10 | // verfiles get created from this 11 | const TEMPLATE: &str = r#"{ 12 | "version": 2, 13 | "data": {} 14 | } 15 | "#; 16 | 17 | /// main data structure 18 | #[derive(Serialize, Deserialize)] 19 | pub struct VerData { 20 | pub data: BTreeMap, 21 | } 22 | 23 | /// package entry structure 24 | #[derive(Clone, Serialize, Deserialize)] 25 | pub struct VerPackage { 26 | pub version: String, 27 | #[serde(default)] 28 | pub gitref: String, 29 | /// url pointing to the release 30 | #[serde(default)] 31 | pub url: String, 32 | } 33 | 34 | /// file structure 35 | #[derive(Serialize, Deserialize)] 36 | pub struct Verfile { 37 | version: u8, 38 | #[serde(flatten)] 39 | pub data: VerData, 40 | } 41 | 42 | /// load the verfiles specified in [crate::config::ConfigTable] 43 | pub async fn load(config_table: &Option) -> error::Result<(Verfile, Verfile)> { 44 | let config_table = config_table.to_owned().ok_or(error::Error::NoConfigTable)?; 45 | 46 | let oldver_path = config::expand_tilde(config_table.oldver.ok_or(error::Error::NoXVer)?)?; 47 | let newver_path = config::expand_tilde(config_table.newver.ok_or(error::Error::NoXVer)?)?; 48 | 49 | let (oldver, newver) = tokio::try_join!( 50 | load_file(Path::new(&oldver_path)), 51 | load_file(Path::new(&newver_path)) 52 | )?; 53 | 54 | if oldver.version != 2 || newver.version != 2 { 55 | return Err(error::Error::VerfileVer); 56 | } 57 | 58 | Ok((oldver, newver)) 59 | } 60 | 61 | /// save changes to the verfiles 62 | pub async fn save( 63 | verfile: &Verfile, 64 | is_oldver: bool, 65 | config_table: &Option, 66 | ) -> error::Result<()> { 67 | let config_table = config_table.to_owned().ok_or(error::Error::NoConfigTable)?; 68 | let path = if is_oldver { 69 | config_table.oldver.ok_or(error::Error::NoXVer)? 70 | } else { 71 | config_table.newver.ok_or(error::Error::NoXVer)? 72 | }; 73 | 74 | let mut file = fs::File::create(Path::new(&config::expand_tilde(path)?)).await?; 75 | let content = format!("{}\n", serde_json::to_string_pretty(&verfile)?); 76 | 77 | file.write_all(content.as_bytes()).await?; 78 | file.shutdown().await?; 79 | 80 | Ok(()) 81 | } 82 | 83 | async fn load_file(path: &Path) -> error::Result { 84 | if !path.exists() { 85 | let mut file = fs::File::create(path).await?; 86 | file.write_all(TEMPLATE.as_bytes()).await?; 87 | file.shutdown().await?; 88 | } 89 | 90 | let content = fs::read_to_string(path).await?; 91 | let verfile: Verfile = serde_json::from_str(&content)?; 92 | 93 | Ok(verfile) 94 | } 95 | 96 | // TODO: tests 97 | --------------------------------------------------------------------------------