├── .github ├── dependabot.yml └── workflows │ ├── audit.yml │ └── general.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Justfile ├── LICENSE.md ├── README.md ├── bacon.toml ├── cliff.toml ├── in.html └── src ├── args.rs ├── cache.rs ├── error.rs ├── feedfetcher.rs ├── lib.rs └── main.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | - package-ecosystem: "cargo" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | push: 6 | paths: 7 | - '**/Cargo.toml' 8 | - '**/Cargo.lock' 9 | jobs: 10 | security_audit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: taiki-e/install-action@cargo-deny 15 | - name: Scan for vulnerabilities 16 | run: cargo deny check advisories -------------------------------------------------------------------------------- /.github/workflows/general.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: dtolnay/rust-toolchain@stable 21 | - uses: Swatinem/rust-cache@v2 22 | - uses: taiki-e/install-action@nextest 23 | - name: Run tests 24 | run: cargo nextest run 25 | 26 | fmt: 27 | name: Rustfmt 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@stable 32 | with: 33 | components: rustfmt 34 | - name: Enforce formatting 35 | run: cargo fmt --check 36 | 37 | clippy: 38 | name: Clippy 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: dtolnay/rust-toolchain@stable 43 | with: 44 | components: clippy 45 | - name: Linting 46 | run: cargo clippy --all-targets --all-features -- -D warnings 47 | 48 | coverage: 49 | name: Code coverage 50 | runs-on: ubuntu-latest 51 | steps: 52 | - name: Checkout repository 53 | uses: actions/checkout@v4 54 | - uses: dtolnay/rust-toolchain@stable 55 | - uses: Swatinem/rust-cache@v2 56 | - uses: taiki-e/install-action@cargo-tarpaulin 57 | - name: Generate code coverage 58 | run: cargo tarpaulin --verbose --workspace 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .openringcache 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 4 | 5 | --- 6 | ## [0.3.7](https://github.com/lukehsiao/openring-rs/compare/v0.3.6..v0.3.7) - 2025-03-22 7 | 8 | ### Documentation 9 | 10 | - **(LICENSE)** use markdown for nicer rendering - ([0cfa7a1](https://github.com/lukehsiao/openring-rs/commit/0cfa7a11d2921a3130d5dae737d50455a7274683)) - Luke Hsiao 11 | 12 | ### Build and Dependencies 13 | 14 | - **(Justfile)** use pedantic on `check` - ([071c354](https://github.com/lukehsiao/openring-rs/commit/071c3548444a29328580a1c91fa7eb02c14628d0)) - Luke Hsiao 15 | - **(deps)** bump serde from 1.0.210 to 1.0.214 - ([aaa343e](https://github.com/lukehsiao/openring-rs/commit/aaa343e8a5c547a086725d3bd06c3f0207a92a39)) - dependabot[bot] 16 | - **(deps)** bump thiserror from 1.0.64 to 1.0.66 - ([2d41eed](https://github.com/lukehsiao/openring-rs/commit/2d41eedaa4985e475be62e6e99168520536f12ad)) - dependabot[bot] 17 | - **(deps)** bump reqwest from 0.12.8 to 0.12.9 - ([322431d](https://github.com/lukehsiao/openring-rs/commit/322431d12a97c79c9e0845567e63081a0c73df3e)) - dependabot[bot] 18 | - **(deps)** bump tokio from 1.40.0 to 1.41.0 - ([5231c0e](https://github.com/lukehsiao/openring-rs/commit/5231c0e33b6cda5b2ce3a2aae5355ad9ef421e64)) - dependabot[bot] 19 | - **(deps)** bump jiff from 0.1.13 to 0.1.14 - ([293d0ab](https://github.com/lukehsiao/openring-rs/commit/293d0abf1036cf23cb18a798d137d7a2f4049d2a)) - dependabot[bot] 20 | - **(deps)** bump thiserror from 1.0.69 to 2.0.3 - ([caa602a](https://github.com/lukehsiao/openring-rs/commit/caa602a5b00c28de36d7f98adefb5b68076e9f72)) - Luke Hsiao 21 | - **(deps)** upgrade all dependencies - ([64e6a57](https://github.com/lukehsiao/openring-rs/commit/64e6a5720dd65563980d781cb24712d9fcb7f7d3)) - Luke Hsiao 22 | - **(deps)** bump thiserror from 2.0.3 to 2.0.11 - ([8dbf8dc](https://github.com/lukehsiao/openring-rs/commit/8dbf8dcc86a85d4b1671dfead4f06d746bbe2b27)) - dependabot[bot] 23 | - **(deps)** bump clap from 4.5.21 to 4.5.27 - ([2391470](https://github.com/lukehsiao/openring-rs/commit/2391470c8cf3c83ee3db8d129c0b8f4eba1e671d)) - dependabot[bot] 24 | - **(deps)** update all dependencies - ([95d1851](https://github.com/lukehsiao/openring-rs/commit/95d185195279989bd294ae37639a18752a5d24b6)) - Luke Hsiao 25 | - **(deps)** update lockfile - ([4833d69](https://github.com/lukehsiao/openring-rs/commit/4833d693bede16f0358920449f58bc7192ee937f)) - Luke Hsiao 26 | - **(deps)** update all dependencies - ([3195e35](https://github.com/lukehsiao/openring-rs/commit/3195e35a6f425f2366007a108c2f75fd4878c036)) - Luke Hsiao 27 | - bump to rust 2024 edition - ([fdf1c40](https://github.com/lukehsiao/openring-rs/commit/fdf1c40123cf44f83c6eb3201005689ab8cb6c09)) - Luke Hsiao 28 | 29 | --- 30 | ## [0.3.6](https://github.com/lukehsiao/openring-rs/compare/v0.3.5..v0.3.6) - 2024-10-11 31 | 32 | This release further adopts `cargo`-style progress, by showing all the remaining URLs in the message, rather than just the most recently fetched URL. 33 | 34 | ### Features 35 | 36 | - show all remaining URLs in progress bar - ([752a433](https://github.com/lukehsiao/openring-rs/commit/752a43387a77d452d0d2bfc209547b2c28bf0c07)) - Luke Hsiao 37 | 38 | ### Refactor 39 | 40 | - use `wide_msg` in progress for auto truncation - ([a237d87](https://github.com/lukehsiao/openring-rs/commit/a237d876773962fbc1a34039aa80981b0824e062)) - Luke Hsiao 41 | - simplify main by using `tracing_log` - ([b7096b1](https://github.com/lukehsiao/openring-rs/commit/b7096b1eb12846447ffc68cc928bc57a524e0bc8)) - Luke Hsiao 42 | 43 | --- 44 | ## [0.3.5](https://github.com/lukehsiao/openring-rs/compare/v0.3.4..v0.3.5) - 2024-10-09 45 | 46 | ### Bug Fixes 47 | 48 | - support compressed feeds (gzip, ztd, brotli, deflate) - ([2d3e467](https://github.com/lukehsiao/openring-rs/commit/2d3e4671d824d02ad787f340de58d16a2648c346)) - Luke Hsiao 49 | 50 | --- 51 | ## [0.3.4](https://github.com/lukehsiao/openring-rs/compare/v0.3.3..v0.3.4) - 2024-10-09 52 | 53 | The primary change of this release is changing to `cargo`-style progress. 54 | It's subjectively a little more explicit and clear. 55 | 56 | ### Documentation 57 | 58 | - **(README)** update Tera link and call out up front - ([b5ac27d](https://github.com/lukehsiao/openring-rs/commit/b5ac27d5c35944c12dddd67922bbaca60c1324a3)) - Luke Hsiao 59 | 60 | ### Refactor 61 | 62 | - simplify progress bar - ([ee98561](https://github.com/lukehsiao/openring-rs/commit/ee98561b4f006085637832cfff080406d8d2fee7)) - Luke Hsiao 63 | - switch to cargo-style progress - ([f1db309](https://github.com/lukehsiao/openring-rs/commit/f1db30995a0df6870c24d3bedc0b9052150d0b1f)) - Luke Hsiao 64 | - print error progress correctly - ([dcd0b6b](https://github.com/lukehsiao/openring-rs/commit/dcd0b6b7b46005994b5d49525b6af35995293c17)) - Luke Hsiao 65 | 66 | ### Build and Dependencies 67 | 68 | - **(deps)** bump clap from 4.5.18 to 4.5.19 - ([8c9431e](https://github.com/lukehsiao/openring-rs/commit/8c9431e4f359191f3bb67394157add536a5b1f66)) - dependabot[bot] 69 | - **(deps)** bump reqwest from 0.12.7 to 0.12.8 - ([2f3484f](https://github.com/lukehsiao/openring-rs/commit/2f3484fb2fd1ba1eadccce2cc97b92b71439d28a)) - dependabot[bot] 70 | - **(deps)** bump all dependencies - ([02c70b5](https://github.com/lukehsiao/openring-rs/commit/02c70b5d197328fd87c1efa83d9dc2f4b76c6cae)) - Luke Hsiao 71 | 72 | --- 73 | ## [0.3.2](https://github.com/lukehsiao/openring-rs/compare/v0.2.5..v0.3.2) - 2024-09-29 74 | 75 | This release is a significant internal refactor that improves performance by fetching _all_ feeds concurrently. 76 | In addition, we also deduplicate feeds to avoid unnecessary fetching. 77 | 78 | ### Bug Fixes 79 | 80 | - do not hold dashmap lock over await call - ([bd706ba](https://github.com/lukehsiao/openring-rs/commit/bd706ba44c7ec15af537477a770024b827448e03)) - Luke Hsiao 81 | 82 | ### Performance 83 | 84 | - switch from `rayon` to `tokio` - ([64a354d](https://github.com/lukehsiao/openring-rs/commit/64a354de873cf06617a81e99c50f3cf701a3c9db)) - Luke Hsiao 85 | - deduplicate feed urls - ([e590e4d](https://github.com/lukehsiao/openring-rs/commit/e590e4d5940a39a201d69dc515e6837d071523cc)) - Luke Hsiao 86 | 87 | --- 88 | ## [0.2.5](https://github.com/lukehsiao/openring-rs/compare/v0.2.4..v0.2.5) - 2024-09-28 89 | 90 | ### Bug Fixes 91 | 92 | - increase timeout from 10s to 30s - ([20f9c55](https://github.com/lukehsiao/openring-rs/commit/20f9c55c69e054d00423c9bbbddce4fe8cd7f3f6)) - Luke Hsiao 93 | 94 | ### Documentation 95 | 96 | - **(README)** add roadmap - ([319c891](https://github.com/lukehsiao/openring-rs/commit/319c8910d72a1b32d55929bf2e250808d746fd34)) - Luke Hsiao 97 | 98 | ### Refactor 99 | 100 | - split into modules - ([fc5c9a5](https://github.com/lukehsiao/openring-rs/commit/fc5c9a50f0b28edabb0b8f6c8a88e481983960b1)) - Luke Hsiao 101 | - pull feed fetching logic into a trait - ([c17f57a](https://github.com/lukehsiao/openring-rs/commit/c17f57aa38273af8b16611ef3234d0a495b58154)) - Luke Hsiao 102 | 103 | --- 104 | ## [0.2.4](https://github.com/lukehsiao/openring-rs/compare/v0.2.3..v0.2.4) - 2024-08-14 105 | 106 | ### Bug Fixes 107 | 108 | - default to 4hrs when receiving a 429 - ([351d563](https://github.com/lukehsiao/openring-rs/commit/351d563efd5c556517ac99d0fdb37e0b5034323b)) - Luke Hsiao 109 | 110 | ### Documentation 111 | 112 | - **(README)** add link to demo of the webring - ([09e1b3c](https://github.com/lukehsiao/openring-rs/commit/09e1b3c58726b3c3345c8bced1bf825174dd4a71)) - Luke Hsiao 113 | 114 | --- 115 | ## [0.2.3](https://github.com/lukehsiao/openring-rs/compare/v0.2.2..v0.2.3) - 2024-08-08 116 | 117 | ### Bug Fixes 118 | 119 | - adjust log levels - ([9c86048](https://github.com/lukehsiao/openring-rs/commit/9c860488cd4a555867bfe95cafc61b53cfd62d5e)) - Luke Hsiao 120 | 121 | --- 122 | ## [0.2.2](https://github.com/lukehsiao/openring-rs/compare/v0.2.1..v0.2.2) - 2024-08-08 123 | 124 | Minor release that now allows feed entries without summary/content. 125 | 126 | ### Bug Fixes 127 | 128 | - allow entries with no summary/content - ([02bcde3](https://github.com/lukehsiao/openring-rs/commit/02bcde3d21ac263ada6f9c97bf28be8faa909562)) - Luke Hsiao 129 | 130 | --- 131 | ## [0.2.1](https://github.com/lukehsiao/openring-rs/compare/v0.2.0..v0.2.1) - 2024-08-08 132 | 133 | **This release adds a nice quality of life feature: local caching.** 134 | 135 | We want to respect `Etag` and `Last-Modified` headers when sending requests to reduce resource strain on the servers providing feeds. 136 | Similarly, we want to respect `Retry-After` if a server provides that header when responding with an HTTP 429. 137 | 138 | This patch respects both by introducing a local cache option in `.openringcache`, which is a simple CSV file with the schema: url, timestamp, retry_after, last_modified, etag, and body, where body is the entire content of the response body last time we fetched the feed. 139 | 140 | With this local cache, if we have a value for Retry-After, we know we were throttled, so we skip sending a request and just use the feed from the cache. 141 | 142 | Otherwise, if we have a cache value, we send a conditional request, setting `If-Modified-Since` and `Etag` headers in the request. 143 | 144 | If we don't have a cache value, we send an unconditional request. 145 | 146 | ### Features 147 | 148 | - add caching options to respect headers - ([0b51bc9](https://github.com/lukehsiao/openring-rs/commit/0b51bc93a0b73a00cbc8c5220cffcc02a92f89ea)) - Luke Hsiao 149 | 150 | ### Build and Dependencies 151 | 152 | - **(deps)** bump clap from 4.5.11 to 4.5.13 - ([2896fc0](https://github.com/lukehsiao/openring-rs/commit/2896fc02e6a8c9710812ea0b9e2c2e6682c7cd64)) - dependabot[bot] 153 | - **(deps)** bump jiff from 0.1.2 to 0.1.3 - ([f0927d2](https://github.com/lukehsiao/openring-rs/commit/f0927d2b2f2429233a8edcc97ff789e33c65074d)) - dependabot[bot] 154 | - **(deps)** bump serde_json from 1.0.121 to 1.0.122 - ([2b5ec04](https://github.com/lukehsiao/openring-rs/commit/2b5ec04296851c77b09cf074733b9faec288d666)) - dependabot[bot] 155 | - **(deps)** upgrade all dependencies - ([32248c0](https://github.com/lukehsiao/openring-rs/commit/32248c0c4059f0eb91947c7129243371d80e50f6)) - Luke Hsiao 156 | - tweak changelog and order of release checks - ([c8c6ebe](https://github.com/lukehsiao/openring-rs/commit/c8c6ebeb25f9f15a4fdeac437a3b2804c92e00b2)) - Luke Hsiao 157 | 158 | --- 159 | ## [0.2.0](https://github.com/lukehsiao/openring-rs/compare/v0.1.15..v0.2.0) - 2024-07-29 160 | 161 | In this release, the only meaningful change is changing from `chrono` to `jiff` as a dependency. 162 | However, this does also rename `article.date` to `article.timestamp` to better reflect reality. 163 | It is likely you will simply need to update your template to `s/article.date/article.timestamp/` and be on your way. 164 | 165 | ### Build and Dependencies 166 | 167 | - **(deps)** [**breaking**] switch from `chrono` to `jiff` - ([485fe4e](https://github.com/lukehsiao/openring-rs/commit/485fe4ef480c9f08a1c895b9e6b75b8c2b6f3774)) - Luke Hsiao 168 | 169 | --- 170 | ## [0.1.15](https://github.com/lukehsiao/openring-rs/compare/v0.1.14..v0.1.15) - 2024-06-04 171 | 172 | ### Dependencies 173 | - Bump to `feed-rs` v2.0.0 - Luke Hsiao 174 | 175 | ### Styling 176 | 177 | - **(README)** 1 sentence per line and consistent indentation - ([04bbb05](https://github.com/lukehsiao/openring-rs/commit/04bbb05abfc296d52963bdf8e36dcbbe6ecc1b98)) - Luke Hsiao 178 | - run rustfmt - ([25413ff](https://github.com/lukehsiao/openring-rs/commit/25413ffb423b7f7bc6d22bd61c4af5b6e97da121)) - Luke Hsiao 179 | 180 | --- 181 | ## [0.1.14](https://github.com/lukehsiao/openring-rs/compare/v0.1.13..v0.1.14) - 2024-01-18 182 | 183 | ### Documentation 184 | 185 | - **(CHANGELOG)** add entry for v0.1.14 - ([487c784](https://github.com/lukehsiao/openring-rs/commit/487c784e84af5c34da6d680625187328a0f101f1)) - Luke Hsiao 186 | - **(README)** link license badge to license - ([0ce9e45](https://github.com/lukehsiao/openring-rs/commit/0ce9e4547946d7fa1aa931adae7d950b4f4a6f7f)) - Luke Hsiao 187 | 188 | ### Refactor 189 | 190 | - default to error-level logs - ([23e355a](https://github.com/lukehsiao/openring-rs/commit/23e355a3fdc39f4e10bc496458b6588e20fb7b85)) - Luke Hsiao 191 | 192 | --- 193 | ## [0.1.13](https://github.com/lukehsiao/openring-rs/compare/v0.1.12..v0.1.13) - 2023-10-12 194 | 195 | ### Bug Fixes 196 | 197 | - make relative urls relative to origin - ([a73455c](https://github.com/lukehsiao/openring-rs/commit/a73455cb14831c3834d8c538949bf698b8296b7b)) - Luke Hsiao 198 | - ignore "self" rel on links - ([5968eda](https://github.com/lukehsiao/openring-rs/commit/5968eda6c7e52071bd871abcc68720c0a00704d1)) - Luke Hsiao 199 | 200 | ### Documentation 201 | 202 | - **(CHANGELOG)** add entry for v0.1.13 - ([64e72e0](https://github.com/lukehsiao/openring-rs/commit/64e72e02847792223c5655dc8c9fbbe547270124)) - Luke Hsiao 203 | 204 | ### Features 205 | 206 | - default to domain name if feed title is empty - ([1b08b27](https://github.com/lukehsiao/openring-rs/commit/1b08b27df8f0f9bbb1ae8284cf0b397e36b00614)) - Luke Hsiao 207 | 208 | --- 209 | ## [0.1.12](https://github.com/lukehsiao/openring-rs/compare/v0.1.11..v0.1.12) - 2023-10-12 210 | 211 | ### Documentation 212 | 213 | - **(CHANGELOG)** add entry for v0.1.12 - ([900117e](https://github.com/lukehsiao/openring-rs/commit/900117e47fd0db76af956c97765ecd15aac0e35c)) - Luke Hsiao 214 | 215 | ### Features 216 | 217 | - support feeds with relative URLs - ([f85009b](https://github.com/lukehsiao/openring-rs/commit/f85009b14763098692bb682e2c51f6bcd9f8b5b3)) - Luke Hsiao 218 | 219 | --- 220 | ## [0.1.11](https://github.com/lukehsiao/openring-rs/compare/v0.1.10..v0.1.11) - 2023-09-07 221 | 222 | ### Bug Fixes 223 | 224 | - log to stderr, not stdout - ([9e8a2d6](https://github.com/lukehsiao/openring-rs/commit/9e8a2d6775dd7f41b067c7b59b56e8ec8ffb0241)) - Luke Hsiao 225 | 226 | ### Documentation 227 | 228 | - **(CHANGELOG)** add entry for v0.1.11 - ([4efddbf](https://github.com/lukehsiao/openring-rs/commit/4efddbf5ff73e8343a663a03b7e8fa07a7de2dea)) - Luke Hsiao 229 | - **(README)** fix grammar error - ([4d1e778](https://github.com/lukehsiao/openring-rs/commit/4d1e7785e7b0bf23910b94d321fd519532f48015)) - Luke Hsiao 230 | - **(README)** suggest using `--locked` on install - ([445c6d6](https://github.com/lukehsiao/openring-rs/commit/445c6d6df40356bcc86de7637aa98bafe825f42d)) - Luke Hsiao 231 | 232 | ### Refactor 233 | 234 | - standardize and clarify logs - ([64da97b](https://github.com/lukehsiao/openring-rs/commit/64da97bf91a1d9abdd73d7fdf09847461dbba48d)) - Luke Hsiao 235 | 236 | --- 237 | ## [0.1.10](https://github.com/lukehsiao/openring-rs/compare/v0.1.9..v0.1.10) - 2023-09-07 238 | 239 | ### Documentation 240 | 241 | - **(CHANGELOG)** add entry for v0.1.10 - ([435f16c](https://github.com/lukehsiao/openring-rs/commit/435f16c2a647894b23f246bea70b8cdea8a38fb7)) - Luke Hsiao 242 | 243 | ### Refactor 244 | 245 | - rename `--urls` to just `--url` - ([178788b](https://github.com/lukehsiao/openring-rs/commit/178788b37e05dbd8db6c2f371473dba0ae4cb739)) - Luke Hsiao 246 | - [**breaking**] switch to `feed-rs` - ([032add1](https://github.com/lukehsiao/openring-rs/commit/032add1034cfc72786957a34b1705606fd1f6488)) - Luke Hsiao 247 | 248 | --- 249 | ## [0.1.9](https://github.com/lukehsiao/openring-rs/compare/v0.1.8..v0.1.9) - 2023-08-11 250 | 251 | ### Documentation 252 | 253 | - **(CHANGELOG)** add entry for v0.1.9 - ([8e694d0](https://github.com/lukehsiao/openring-rs/commit/8e694d063d1be2cb73e5fabe6b72f23c836d94ee)) - Luke Hsiao 254 | 255 | ### Features 256 | 257 | - provide `miette`-powered error diagnostics - ([88c63a0](https://github.com/lukehsiao/openring-rs/commit/88c63a00fc0c28cedbb77a7debcd2d49c728419c)) - Luke Hsiao 258 | 259 | --- 260 | ## [0.1.8](https://github.com/lukehsiao/openring-rs/compare/v0.1.7..v0.1.8) - 2023-06-21 261 | 262 | ### Documentation 263 | 264 | - **(CHANGELOG)** add entry for v0.1.8 - ([cd9ed3c](https://github.com/lukehsiao/openring-rs/commit/cd9ed3c97d40a82356f3cec19c2c3a89c6eb31ca)) - Luke Hsiao 265 | - **(README)** add badges - ([5334775](https://github.com/lukehsiao/openring-rs/commit/5334775b30468df49ae0d9b6e56c109b590b4e47)) - Luke Hsiao 266 | 267 | --- 268 | ## [0.1.7](https://github.com/lukehsiao/openring-rs/compare/v0.1.6..v0.1.7) - 2023-05-21 269 | 270 | ### Documentation 271 | 272 | - **(CHANGELOG)** add entry for v0.1.7 - ([231a812](https://github.com/lukehsiao/openring-rs/commit/231a812f0de14e1a6bbcb5153c7bc5cbca6de3fd)) - Luke Hsiao 273 | 274 | ### Features 275 | 276 | - support naive datetime of form `%Y-%m-%dT%H:%M:%S` - ([a1e2d4d](https://github.com/lukehsiao/openring-rs/commit/a1e2d4dd8698bad6a09086cbf787e0d370180e75)) - Luke Hsiao 277 | 278 | ### Refactor 279 | 280 | - use WarnLevel by default - ([7bf66d6](https://github.com/lukehsiao/openring-rs/commit/7bf66d6cdc0c313d1b60ee7462fa6cd12fafbfc6)) - Luke Hsiao 281 | - s/unable/failed/ - ([308d08f](https://github.com/lukehsiao/openring-rs/commit/308d08fc790f6412768bcff4022ff860a5cf5f12)) - Luke Hsiao 282 | 283 | --- 284 | ## [0.1.6](https://github.com/lukehsiao/openring-rs/compare/v0.1.5..v0.1.6) - 2022-12-11 285 | 286 | ### Documentation 287 | 288 | - **(CHANGELOG)** add entry for v0.1.6 - ([811582c](https://github.com/lukehsiao/openring-rs/commit/811582c38b254e62266d5c18f739e68de0ac1c73)) - Luke Hsiao 289 | 290 | ### Features 291 | 292 | - add `--before` to allow filtering to posts before a given date - ([4d42a33](https://github.com/lukehsiao/openring-rs/commit/4d42a33202bcb784216f75fb03f94d63a48ec540)) - Luke Hsiao 293 | 294 | --- 295 | ## [0.1.5](https://github.com/lukehsiao/openring-rs/compare/v0.1.4..v0.1.5) - 2022-11-26 296 | 297 | ### Bug Fixes 298 | 299 | - trim whitespace around summaries - ([14e37d7](https://github.com/lukehsiao/openring-rs/commit/14e37d7f1b3bf58394f3eb51b2f8e38cd10fb561)) - Luke Hsiao 300 | 301 | ### Documentation 302 | 303 | - **(CHANGELOG)** add entry for v0.1.5 - ([e56d89f](https://github.com/lukehsiao/openring-rs/commit/e56d89f97009727548b53f84f1e99e512d8b784d)) - Luke Hsiao 304 | 305 | --- 306 | ## [0.1.4](https://github.com/lukehsiao/openring-rs/compare/v0.1.3..v0.1.4) - 2022-11-26 307 | 308 | ### Bug Fixes 309 | 310 | - properly decode html entities - ([3ddbcd8](https://github.com/lukehsiao/openring-rs/commit/3ddbcd801166c9b33385027a7042c66971ca899a)) - Luke Hsiao 311 | 312 | ### Documentation 313 | 314 | - **(CHANGELOG)** add entry for v0.1.4 - ([8c9290a](https://github.com/lukehsiao/openring-rs/commit/8c9290a3f14403bc68ae20acc4c091dcf061e372)) - Luke Hsiao 315 | 316 | --- 317 | ## [0.1.3](https://github.com/lukehsiao/openring-rs/compare/v0.1.2..v0.1.3) - 2022-11-26 318 | 319 | ### Bug Fixes 320 | 321 | - include the semicolon when stripping nbsp - ([d9b9fd4](https://github.com/lukehsiao/openring-rs/commit/d9b9fd42ec6120d4dbf9689268f53c072e59d18d)) - Luke Hsiao 322 | 323 | ### Documentation 324 | 325 | - **(CHANGELOG)** add entry for v0.1.3 - ([c9dda71](https://github.com/lukehsiao/openring-rs/commit/c9dda71b9f7a798be65399787cf9c6d738eeaa22)) - Luke Hsiao 326 | 327 | --- 328 | ## [0.1.2](https://github.com/lukehsiao/openring-rs/compare/v0.1.1..v0.1.2) - 2022-11-26 329 | 330 | ### Bug Fixes 331 | 332 | - strip non-breaking spaces from summary - ([e196901](https://github.com/lukehsiao/openring-rs/commit/e19690173dc5d57ebac0b791fa29d15932ca8f7b)) - Luke Hsiao 333 | - use last link in atom entry for blogspot - ([d169aef](https://github.com/lukehsiao/openring-rs/commit/d169aef6858c0307498ca5963528e2de5a3e4f97)) - Luke Hsiao 334 | - default to using the alternate url - ([f314f02](https://github.com/lukehsiao/openring-rs/commit/f314f02382d7bed889ce36897c3184b6a22d7a5e)) - Luke Hsiao 335 | 336 | ### Documentation 337 | 338 | - **(CHANGELOG)** add entry for v0.1.2 - ([aab19db](https://github.com/lukehsiao/openring-rs/commit/aab19db19683218ca41790ce6e0e4eae80b48d32)) - Luke Hsiao 339 | - **(README)** use unicode icon directly - ([5de0aef](https://github.com/lukehsiao/openring-rs/commit/5de0aefaf60622d689aa387c39c0bfa56e657584)) - Luke Hsiao 340 | 341 | --- 342 | ## [0.1.1](https://github.com/lukehsiao/openring-rs/compare/v0.1.0..v0.1.1) - 2022-11-26 343 | 344 | ### Documentation 345 | 346 | - **(CHANGELOG)** add entry for v0.1.1 - ([1052862](https://github.com/lukehsiao/openring-rs/commit/1052862697e4891843b8ee75f101a5e19ad016a3)) - Luke Hsiao 347 | - **(README)** add installation instructions - ([9d41547](https://github.com/lukehsiao/openring-rs/commit/9d41547c5ca890256da0f87e0f3540738570307f)) - Luke Hsiao 348 | - **(README)** use a fancier header - ([3e171a7](https://github.com/lukehsiao/openring-rs/commit/3e171a7bd3a4252bcf45b42af724c5101347a9fc)) - Luke Hsiao 349 | 350 | --- 351 | ## [0.1.0] - 2022-09-17 352 | 353 | ### Bug Fixes 354 | 355 | - switch to fixedoffset and support more date formats - ([c673d77](https://github.com/lukehsiao/openring-rs/commit/c673d774ee1d2aa84205f340cf08cf6511fb9ebd)) - Luke Hsiao 356 | 357 | ### Documentation 358 | 359 | - **(README)** add initial README - ([ab76822](https://github.com/lukehsiao/openring-rs/commit/ab76822ccf256d39089b39ea39ce518c5c66b035)) - Luke Hsiao 360 | - **(README)** update option help messages - ([94451bd](https://github.com/lukehsiao/openring-rs/commit/94451bd2bd53c1854a00ab8612eba131128742e9)) - Luke Hsiao 361 | 362 | ### Features 363 | 364 | - finish initial implementation - ([fc58c31](https://github.com/lukehsiao/openring-rs/commit/fc58c312d2d1e0f7281d24d026b8a30bb8b69512)) - Luke Hsiao 365 | - provide basic progress bar with indicatif - ([f1efb04](https://github.com/lukehsiao/openring-rs/commit/f1efb0458d5057aa03728c96c5b502795b8fea63)) - Luke Hsiao 366 | - show actual urls with indicatif progress - ([eb54b01](https://github.com/lukehsiao/openring-rs/commit/eb54b018e139690d320c0b785a81db3a93acc1b0)) - Luke Hsiao 367 | 368 | ### Performance 369 | 370 | - parallelize requests with rayon - ([7169222](https://github.com/lukehsiao/openring-rs/commit/716922207826c341715f0d9b9f9a3e46811a52de)) - Luke Hsiao 371 | 372 | ### Refactor 373 | 374 | - add basic arguments to match openring - ([b5dec8b](https://github.com/lukehsiao/openring-rs/commit/b5dec8b29a1d277e16ccb2e1228a442cc7239171)) - Luke Hsiao 375 | - add basic logging and anyhow - ([a3dd708](https://github.com/lukehsiao/openring-rs/commit/a3dd708aca4716976ab28cd34a10a49737e10a3c)) - Luke Hsiao 376 | - move core impl into lib.rs - ([2cbf54a](https://github.com/lukehsiao/openring-rs/commit/2cbf54a4e1fa143a887da5fd628bba7e01b5fa0d)) - Luke Hsiao 377 | - allow parsing a url file - ([3a2374d](https://github.com/lukehsiao/openring-rs/commit/3a2374dd2238b1e8e02b2cccd66f05fe8b6d2aa4)) - Luke Hsiao 378 | - setup structure for tera - ([435b181](https://github.com/lukehsiao/openring-rs/commit/435b181c044e9f69ee74afa9d136dc4f98ac28a5)) - Luke Hsiao 379 | - error if no feed urls are provided - ([65393eb](https://github.com/lukehsiao/openring-rs/commit/65393eb0c96d203b6b459667e067feb3594cf247)) - Luke Hsiao 380 | 381 | -------------------------------------------------------------------------------- /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 = "alloc-no-stdlib" 31 | version = "2.0.4" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 34 | 35 | [[package]] 36 | name = "alloc-stdlib" 37 | version = "0.2.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 40 | dependencies = [ 41 | "alloc-no-stdlib", 42 | ] 43 | 44 | [[package]] 45 | name = "ammonia" 46 | version = "4.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" 49 | dependencies = [ 50 | "html5ever", 51 | "maplit", 52 | "once_cell", 53 | "tendril", 54 | "url", 55 | ] 56 | 57 | [[package]] 58 | name = "android-tzdata" 59 | version = "0.1.1" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 62 | 63 | [[package]] 64 | name = "android_system_properties" 65 | version = "0.1.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 68 | dependencies = [ 69 | "libc", 70 | ] 71 | 72 | [[package]] 73 | name = "anstream" 74 | version = "0.6.18" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 77 | dependencies = [ 78 | "anstyle", 79 | "anstyle-parse", 80 | "anstyle-query", 81 | "anstyle-wincon", 82 | "colorchoice", 83 | "is_terminal_polyfill", 84 | "utf8parse", 85 | ] 86 | 87 | [[package]] 88 | name = "anstyle" 89 | version = "1.0.10" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 92 | 93 | [[package]] 94 | name = "anstyle-parse" 95 | version = "0.2.6" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 98 | dependencies = [ 99 | "utf8parse", 100 | ] 101 | 102 | [[package]] 103 | name = "anstyle-query" 104 | version = "1.1.2" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 107 | dependencies = [ 108 | "windows-sys 0.59.0", 109 | ] 110 | 111 | [[package]] 112 | name = "anstyle-wincon" 113 | version = "3.0.7" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 116 | dependencies = [ 117 | "anstyle", 118 | "once_cell", 119 | "windows-sys 0.59.0", 120 | ] 121 | 122 | [[package]] 123 | name = "async-compression" 124 | version = "0.4.21" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "c0cf008e5e1a9e9e22a7d3c9a4992e21a350290069e36d8fb72304ed17e8f2d2" 127 | dependencies = [ 128 | "brotli", 129 | "flate2", 130 | "futures-core", 131 | "memchr", 132 | "pin-project-lite", 133 | "tokio", 134 | "zstd", 135 | "zstd-safe", 136 | ] 137 | 138 | [[package]] 139 | name = "atomic-waker" 140 | version = "1.1.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 143 | 144 | [[package]] 145 | name = "autocfg" 146 | version = "1.4.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 149 | 150 | [[package]] 151 | name = "backtrace" 152 | version = "0.3.74" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 155 | dependencies = [ 156 | "addr2line", 157 | "cfg-if", 158 | "libc", 159 | "miniz_oxide", 160 | "object", 161 | "rustc-demangle", 162 | "windows-targets 0.52.6", 163 | ] 164 | 165 | [[package]] 166 | name = "backtrace-ext" 167 | version = "0.2.1" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 170 | dependencies = [ 171 | "backtrace", 172 | ] 173 | 174 | [[package]] 175 | name = "base64" 176 | version = "0.22.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 179 | 180 | [[package]] 181 | name = "bitflags" 182 | version = "2.9.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 185 | 186 | [[package]] 187 | name = "block-buffer" 188 | version = "0.10.4" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 191 | dependencies = [ 192 | "generic-array", 193 | ] 194 | 195 | [[package]] 196 | name = "brotli" 197 | version = "7.0.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" 200 | dependencies = [ 201 | "alloc-no-stdlib", 202 | "alloc-stdlib", 203 | "brotli-decompressor", 204 | ] 205 | 206 | [[package]] 207 | name = "brotli-decompressor" 208 | version = "4.0.2" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" 211 | dependencies = [ 212 | "alloc-no-stdlib", 213 | "alloc-stdlib", 214 | ] 215 | 216 | [[package]] 217 | name = "bstr" 218 | version = "1.11.3" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" 221 | dependencies = [ 222 | "memchr", 223 | "serde", 224 | ] 225 | 226 | [[package]] 227 | name = "bumpalo" 228 | version = "3.17.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 231 | 232 | [[package]] 233 | name = "bytes" 234 | version = "1.10.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 237 | 238 | [[package]] 239 | name = "cc" 240 | version = "1.2.17" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" 243 | dependencies = [ 244 | "jobserver", 245 | "libc", 246 | "shlex", 247 | ] 248 | 249 | [[package]] 250 | name = "cfg-if" 251 | version = "1.0.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 254 | 255 | [[package]] 256 | name = "chrono" 257 | version = "0.4.40" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" 260 | dependencies = [ 261 | "android-tzdata", 262 | "iana-time-zone", 263 | "js-sys", 264 | "num-traits", 265 | "serde", 266 | "wasm-bindgen", 267 | "windows-link", 268 | ] 269 | 270 | [[package]] 271 | name = "chrono-tz" 272 | version = "0.9.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" 275 | dependencies = [ 276 | "chrono", 277 | "chrono-tz-build", 278 | "phf", 279 | ] 280 | 281 | [[package]] 282 | name = "chrono-tz-build" 283 | version = "0.3.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" 286 | dependencies = [ 287 | "parse-zoneinfo", 288 | "phf", 289 | "phf_codegen", 290 | ] 291 | 292 | [[package]] 293 | name = "clap" 294 | version = "4.5.32" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" 297 | dependencies = [ 298 | "clap_builder", 299 | "clap_derive", 300 | ] 301 | 302 | [[package]] 303 | name = "clap-verbosity-flag" 304 | version = "3.0.2" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "2678fade3b77aa3a8ff3aae87e9c008d3fb00473a41c71fbf74e91c8c7b37e84" 307 | dependencies = [ 308 | "clap", 309 | "log", 310 | ] 311 | 312 | [[package]] 313 | name = "clap_builder" 314 | version = "4.5.32" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" 317 | dependencies = [ 318 | "anstream", 319 | "anstyle", 320 | "clap_lex", 321 | "strsim", 322 | "terminal_size", 323 | ] 324 | 325 | [[package]] 326 | name = "clap_derive" 327 | version = "4.5.32" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 330 | dependencies = [ 331 | "heck", 332 | "proc-macro2", 333 | "quote", 334 | "syn", 335 | ] 336 | 337 | [[package]] 338 | name = "clap_lex" 339 | version = "0.7.4" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 342 | 343 | [[package]] 344 | name = "colorchoice" 345 | version = "1.0.3" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 348 | 349 | [[package]] 350 | name = "console" 351 | version = "0.15.11" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" 354 | dependencies = [ 355 | "encode_unicode", 356 | "libc", 357 | "once_cell", 358 | "unicode-width 0.2.0", 359 | "windows-sys 0.59.0", 360 | ] 361 | 362 | [[package]] 363 | name = "core-foundation" 364 | version = "0.9.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 367 | dependencies = [ 368 | "core-foundation-sys", 369 | "libc", 370 | ] 371 | 372 | [[package]] 373 | name = "core-foundation-sys" 374 | version = "0.8.7" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 377 | 378 | [[package]] 379 | name = "cpufeatures" 380 | version = "0.2.17" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 383 | dependencies = [ 384 | "libc", 385 | ] 386 | 387 | [[package]] 388 | name = "crc32fast" 389 | version = "1.4.2" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 392 | dependencies = [ 393 | "cfg-if", 394 | ] 395 | 396 | [[package]] 397 | name = "crossbeam-deque" 398 | version = "0.8.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 401 | dependencies = [ 402 | "crossbeam-epoch", 403 | "crossbeam-utils", 404 | ] 405 | 406 | [[package]] 407 | name = "crossbeam-epoch" 408 | version = "0.9.18" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 411 | dependencies = [ 412 | "crossbeam-utils", 413 | ] 414 | 415 | [[package]] 416 | name = "crossbeam-utils" 417 | version = "0.8.21" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 420 | 421 | [[package]] 422 | name = "crypto-common" 423 | version = "0.1.6" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 426 | dependencies = [ 427 | "generic-array", 428 | "typenum", 429 | ] 430 | 431 | [[package]] 432 | name = "csv" 433 | version = "1.3.1" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" 436 | dependencies = [ 437 | "csv-core", 438 | "itoa", 439 | "ryu", 440 | "serde", 441 | ] 442 | 443 | [[package]] 444 | name = "csv-core" 445 | version = "0.1.12" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" 448 | dependencies = [ 449 | "memchr", 450 | ] 451 | 452 | [[package]] 453 | name = "dashmap" 454 | version = "6.1.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 457 | dependencies = [ 458 | "cfg-if", 459 | "crossbeam-utils", 460 | "hashbrown 0.14.5", 461 | "lock_api", 462 | "once_cell", 463 | "parking_lot_core", 464 | ] 465 | 466 | [[package]] 467 | name = "deunicode" 468 | version = "1.6.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d" 471 | 472 | [[package]] 473 | name = "digest" 474 | version = "0.10.7" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 477 | dependencies = [ 478 | "block-buffer", 479 | "crypto-common", 480 | ] 481 | 482 | [[package]] 483 | name = "displaydoc" 484 | version = "0.2.5" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 487 | dependencies = [ 488 | "proc-macro2", 489 | "quote", 490 | "syn", 491 | ] 492 | 493 | [[package]] 494 | name = "encode_unicode" 495 | version = "1.0.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 498 | 499 | [[package]] 500 | name = "encoding_rs" 501 | version = "0.8.35" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 504 | dependencies = [ 505 | "cfg-if", 506 | ] 507 | 508 | [[package]] 509 | name = "equivalent" 510 | version = "1.0.2" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 513 | 514 | [[package]] 515 | name = "errno" 516 | version = "0.3.10" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 519 | dependencies = [ 520 | "libc", 521 | "windows-sys 0.59.0", 522 | ] 523 | 524 | [[package]] 525 | name = "fastrand" 526 | version = "2.3.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 529 | 530 | [[package]] 531 | name = "feed-rs" 532 | version = "2.3.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "e4c0591d23efd0d595099af69a31863ac1823046b1b021e3b06ba3aae7e00991" 535 | dependencies = [ 536 | "chrono", 537 | "mediatype", 538 | "quick-xml", 539 | "regex", 540 | "serde", 541 | "serde_json", 542 | "siphasher", 543 | "url", 544 | "uuid", 545 | ] 546 | 547 | [[package]] 548 | name = "flate2" 549 | version = "1.1.0" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" 552 | dependencies = [ 553 | "crc32fast", 554 | "miniz_oxide", 555 | ] 556 | 557 | [[package]] 558 | name = "fnv" 559 | version = "1.0.7" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 562 | 563 | [[package]] 564 | name = "foreign-types" 565 | version = "0.3.2" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 568 | dependencies = [ 569 | "foreign-types-shared", 570 | ] 571 | 572 | [[package]] 573 | name = "foreign-types-shared" 574 | version = "0.1.1" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 577 | 578 | [[package]] 579 | name = "form_urlencoded" 580 | version = "1.2.1" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 583 | dependencies = [ 584 | "percent-encoding", 585 | ] 586 | 587 | [[package]] 588 | name = "futf" 589 | version = "0.1.5" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" 592 | dependencies = [ 593 | "mac", 594 | "new_debug_unreachable", 595 | ] 596 | 597 | [[package]] 598 | name = "futures-channel" 599 | version = "0.3.31" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 602 | dependencies = [ 603 | "futures-core", 604 | ] 605 | 606 | [[package]] 607 | name = "futures-core" 608 | version = "0.3.31" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 611 | 612 | [[package]] 613 | name = "futures-sink" 614 | version = "0.3.31" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 617 | 618 | [[package]] 619 | name = "futures-task" 620 | version = "0.3.31" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 623 | 624 | [[package]] 625 | name = "futures-util" 626 | version = "0.3.31" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 629 | dependencies = [ 630 | "futures-core", 631 | "futures-task", 632 | "pin-project-lite", 633 | "pin-utils", 634 | ] 635 | 636 | [[package]] 637 | name = "generic-array" 638 | version = "0.14.7" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 641 | dependencies = [ 642 | "typenum", 643 | "version_check", 644 | ] 645 | 646 | [[package]] 647 | name = "getrandom" 648 | version = "0.2.15" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 651 | dependencies = [ 652 | "cfg-if", 653 | "libc", 654 | "wasi 0.11.0+wasi-snapshot-preview1", 655 | ] 656 | 657 | [[package]] 658 | name = "getrandom" 659 | version = "0.3.2" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 662 | dependencies = [ 663 | "cfg-if", 664 | "libc", 665 | "r-efi", 666 | "wasi 0.14.2+wasi-0.2.4", 667 | ] 668 | 669 | [[package]] 670 | name = "gimli" 671 | version = "0.31.1" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 674 | 675 | [[package]] 676 | name = "globset" 677 | version = "0.4.16" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 680 | dependencies = [ 681 | "aho-corasick", 682 | "bstr", 683 | "log", 684 | "regex-automata 0.4.9", 685 | "regex-syntax 0.8.5", 686 | ] 687 | 688 | [[package]] 689 | name = "globwalk" 690 | version = "0.9.1" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" 693 | dependencies = [ 694 | "bitflags", 695 | "ignore", 696 | "walkdir", 697 | ] 698 | 699 | [[package]] 700 | name = "h2" 701 | version = "0.4.8" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" 704 | dependencies = [ 705 | "atomic-waker", 706 | "bytes", 707 | "fnv", 708 | "futures-core", 709 | "futures-sink", 710 | "http", 711 | "indexmap", 712 | "slab", 713 | "tokio", 714 | "tokio-util", 715 | "tracing", 716 | ] 717 | 718 | [[package]] 719 | name = "hashbrown" 720 | version = "0.14.5" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 723 | 724 | [[package]] 725 | name = "hashbrown" 726 | version = "0.15.2" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 729 | 730 | [[package]] 731 | name = "heck" 732 | version = "0.5.0" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 735 | 736 | [[package]] 737 | name = "hermit-abi" 738 | version = "0.5.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" 741 | 742 | [[package]] 743 | name = "html-escape" 744 | version = "0.2.13" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" 747 | dependencies = [ 748 | "utf8-width", 749 | ] 750 | 751 | [[package]] 752 | name = "html5ever" 753 | version = "0.27.0" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" 756 | dependencies = [ 757 | "log", 758 | "mac", 759 | "markup5ever", 760 | "proc-macro2", 761 | "quote", 762 | "syn", 763 | ] 764 | 765 | [[package]] 766 | name = "http" 767 | version = "1.3.1" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 770 | dependencies = [ 771 | "bytes", 772 | "fnv", 773 | "itoa", 774 | ] 775 | 776 | [[package]] 777 | name = "http-body" 778 | version = "1.0.1" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 781 | dependencies = [ 782 | "bytes", 783 | "http", 784 | ] 785 | 786 | [[package]] 787 | name = "http-body-util" 788 | version = "0.1.3" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 791 | dependencies = [ 792 | "bytes", 793 | "futures-core", 794 | "http", 795 | "http-body", 796 | "pin-project-lite", 797 | ] 798 | 799 | [[package]] 800 | name = "httparse" 801 | version = "1.10.1" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 804 | 805 | [[package]] 806 | name = "humansize" 807 | version = "2.1.3" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" 810 | dependencies = [ 811 | "libm", 812 | ] 813 | 814 | [[package]] 815 | name = "humantime" 816 | version = "2.2.0" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" 819 | 820 | [[package]] 821 | name = "hyper" 822 | version = "1.6.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 825 | dependencies = [ 826 | "bytes", 827 | "futures-channel", 828 | "futures-util", 829 | "h2", 830 | "http", 831 | "http-body", 832 | "httparse", 833 | "itoa", 834 | "pin-project-lite", 835 | "smallvec", 836 | "tokio", 837 | "want", 838 | ] 839 | 840 | [[package]] 841 | name = "hyper-rustls" 842 | version = "0.27.5" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 845 | dependencies = [ 846 | "futures-util", 847 | "http", 848 | "hyper", 849 | "hyper-util", 850 | "rustls", 851 | "rustls-pki-types", 852 | "tokio", 853 | "tokio-rustls", 854 | "tower-service", 855 | ] 856 | 857 | [[package]] 858 | name = "hyper-tls" 859 | version = "0.6.0" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 862 | dependencies = [ 863 | "bytes", 864 | "http-body-util", 865 | "hyper", 866 | "hyper-util", 867 | "native-tls", 868 | "tokio", 869 | "tokio-native-tls", 870 | "tower-service", 871 | ] 872 | 873 | [[package]] 874 | name = "hyper-util" 875 | version = "0.1.10" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 878 | dependencies = [ 879 | "bytes", 880 | "futures-channel", 881 | "futures-util", 882 | "http", 883 | "http-body", 884 | "hyper", 885 | "pin-project-lite", 886 | "socket2", 887 | "tokio", 888 | "tower-service", 889 | "tracing", 890 | ] 891 | 892 | [[package]] 893 | name = "iana-time-zone" 894 | version = "0.1.61" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 897 | dependencies = [ 898 | "android_system_properties", 899 | "core-foundation-sys", 900 | "iana-time-zone-haiku", 901 | "js-sys", 902 | "wasm-bindgen", 903 | "windows-core", 904 | ] 905 | 906 | [[package]] 907 | name = "iana-time-zone-haiku" 908 | version = "0.1.2" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 911 | dependencies = [ 912 | "cc", 913 | ] 914 | 915 | [[package]] 916 | name = "icu_collections" 917 | version = "1.5.0" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 920 | dependencies = [ 921 | "displaydoc", 922 | "yoke", 923 | "zerofrom", 924 | "zerovec", 925 | ] 926 | 927 | [[package]] 928 | name = "icu_locid" 929 | version = "1.5.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 932 | dependencies = [ 933 | "displaydoc", 934 | "litemap", 935 | "tinystr", 936 | "writeable", 937 | "zerovec", 938 | ] 939 | 940 | [[package]] 941 | name = "icu_locid_transform" 942 | version = "1.5.0" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 945 | dependencies = [ 946 | "displaydoc", 947 | "icu_locid", 948 | "icu_locid_transform_data", 949 | "icu_provider", 950 | "tinystr", 951 | "zerovec", 952 | ] 953 | 954 | [[package]] 955 | name = "icu_locid_transform_data" 956 | version = "1.5.0" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 959 | 960 | [[package]] 961 | name = "icu_normalizer" 962 | version = "1.5.0" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 965 | dependencies = [ 966 | "displaydoc", 967 | "icu_collections", 968 | "icu_normalizer_data", 969 | "icu_properties", 970 | "icu_provider", 971 | "smallvec", 972 | "utf16_iter", 973 | "utf8_iter", 974 | "write16", 975 | "zerovec", 976 | ] 977 | 978 | [[package]] 979 | name = "icu_normalizer_data" 980 | version = "1.5.0" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 983 | 984 | [[package]] 985 | name = "icu_properties" 986 | version = "1.5.1" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 989 | dependencies = [ 990 | "displaydoc", 991 | "icu_collections", 992 | "icu_locid_transform", 993 | "icu_properties_data", 994 | "icu_provider", 995 | "tinystr", 996 | "zerovec", 997 | ] 998 | 999 | [[package]] 1000 | name = "icu_properties_data" 1001 | version = "1.5.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 1004 | 1005 | [[package]] 1006 | name = "icu_provider" 1007 | version = "1.5.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 1010 | dependencies = [ 1011 | "displaydoc", 1012 | "icu_locid", 1013 | "icu_provider_macros", 1014 | "stable_deref_trait", 1015 | "tinystr", 1016 | "writeable", 1017 | "yoke", 1018 | "zerofrom", 1019 | "zerovec", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "icu_provider_macros" 1024 | version = "1.5.0" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 1027 | dependencies = [ 1028 | "proc-macro2", 1029 | "quote", 1030 | "syn", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "idna" 1035 | version = "1.0.3" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 1038 | dependencies = [ 1039 | "idna_adapter", 1040 | "smallvec", 1041 | "utf8_iter", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "idna_adapter" 1046 | version = "1.2.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 1049 | dependencies = [ 1050 | "icu_normalizer", 1051 | "icu_properties", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "ignore" 1056 | version = "0.4.23" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" 1059 | dependencies = [ 1060 | "crossbeam-deque", 1061 | "globset", 1062 | "log", 1063 | "memchr", 1064 | "regex-automata 0.4.9", 1065 | "same-file", 1066 | "walkdir", 1067 | "winapi-util", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "indexmap" 1072 | version = "2.8.0" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 1075 | dependencies = [ 1076 | "equivalent", 1077 | "hashbrown 0.15.2", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "indicatif" 1082 | version = "0.17.11" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" 1085 | dependencies = [ 1086 | "console", 1087 | "number_prefix", 1088 | "portable-atomic", 1089 | "unicode-width 0.2.0", 1090 | "web-time", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "ipnet" 1095 | version = "2.11.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 1098 | 1099 | [[package]] 1100 | name = "is-terminal" 1101 | version = "0.4.16" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" 1104 | dependencies = [ 1105 | "hermit-abi", 1106 | "libc", 1107 | "windows-sys 0.59.0", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "is_ci" 1112 | version = "1.2.0" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 1115 | 1116 | [[package]] 1117 | name = "is_terminal_polyfill" 1118 | version = "1.70.1" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 1121 | 1122 | [[package]] 1123 | name = "itoa" 1124 | version = "1.0.15" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1127 | 1128 | [[package]] 1129 | name = "jiff" 1130 | version = "0.2.4" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" 1133 | dependencies = [ 1134 | "jiff-static", 1135 | "jiff-tzdb-platform", 1136 | "log", 1137 | "portable-atomic", 1138 | "portable-atomic-util", 1139 | "serde", 1140 | "windows-sys 0.59.0", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "jiff-static" 1145 | version = "0.2.4" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" 1148 | dependencies = [ 1149 | "proc-macro2", 1150 | "quote", 1151 | "syn", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "jiff-tzdb" 1156 | version = "0.1.3" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "962e1dfe9b2d75a84536cf5bf5eaaa4319aa7906c7160134a22883ac316d5f31" 1159 | 1160 | [[package]] 1161 | name = "jiff-tzdb-platform" 1162 | version = "0.1.2" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e" 1165 | dependencies = [ 1166 | "jiff-tzdb", 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "jobserver" 1171 | version = "0.1.32" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 1174 | dependencies = [ 1175 | "libc", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "js-sys" 1180 | version = "0.3.77" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 1183 | dependencies = [ 1184 | "once_cell", 1185 | "wasm-bindgen", 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "lazy_static" 1190 | version = "1.5.0" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1193 | 1194 | [[package]] 1195 | name = "libc" 1196 | version = "0.2.171" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 1199 | 1200 | [[package]] 1201 | name = "libm" 1202 | version = "0.2.11" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" 1205 | 1206 | [[package]] 1207 | name = "linux-raw-sys" 1208 | version = "0.9.3" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" 1211 | 1212 | [[package]] 1213 | name = "litemap" 1214 | version = "0.7.5" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 1217 | 1218 | [[package]] 1219 | name = "lock_api" 1220 | version = "0.4.12" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1223 | dependencies = [ 1224 | "autocfg", 1225 | "scopeguard", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "log" 1230 | version = "0.4.26" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 1233 | 1234 | [[package]] 1235 | name = "mac" 1236 | version = "0.1.1" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 1239 | 1240 | [[package]] 1241 | name = "maplit" 1242 | version = "1.0.2" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 1245 | 1246 | [[package]] 1247 | name = "markup5ever" 1248 | version = "0.12.1" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" 1251 | dependencies = [ 1252 | "log", 1253 | "phf", 1254 | "phf_codegen", 1255 | "string_cache", 1256 | "string_cache_codegen", 1257 | "tendril", 1258 | ] 1259 | 1260 | [[package]] 1261 | name = "matchers" 1262 | version = "0.1.0" 1263 | source = "registry+https://github.com/rust-lang/crates.io-index" 1264 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1265 | dependencies = [ 1266 | "regex-automata 0.1.10", 1267 | ] 1268 | 1269 | [[package]] 1270 | name = "mediatype" 1271 | version = "0.19.20" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" 1274 | dependencies = [ 1275 | "serde", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "memchr" 1280 | version = "2.7.4" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1283 | 1284 | [[package]] 1285 | name = "miette" 1286 | version = "7.5.0" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" 1289 | dependencies = [ 1290 | "backtrace", 1291 | "backtrace-ext", 1292 | "cfg-if", 1293 | "miette-derive", 1294 | "owo-colors", 1295 | "supports-color", 1296 | "supports-hyperlinks", 1297 | "supports-unicode", 1298 | "terminal_size", 1299 | "textwrap", 1300 | "thiserror 1.0.69", 1301 | "unicode-width 0.1.14", 1302 | ] 1303 | 1304 | [[package]] 1305 | name = "miette-derive" 1306 | version = "7.5.0" 1307 | source = "registry+https://github.com/rust-lang/crates.io-index" 1308 | checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" 1309 | dependencies = [ 1310 | "proc-macro2", 1311 | "quote", 1312 | "syn", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "mime" 1317 | version = "0.3.17" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1320 | 1321 | [[package]] 1322 | name = "miniz_oxide" 1323 | version = "0.8.5" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 1326 | dependencies = [ 1327 | "adler2", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "mio" 1332 | version = "1.0.3" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 1335 | dependencies = [ 1336 | "libc", 1337 | "wasi 0.11.0+wasi-snapshot-preview1", 1338 | "windows-sys 0.52.0", 1339 | ] 1340 | 1341 | [[package]] 1342 | name = "native-tls" 1343 | version = "0.2.14" 1344 | source = "registry+https://github.com/rust-lang/crates.io-index" 1345 | checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 1346 | dependencies = [ 1347 | "libc", 1348 | "log", 1349 | "openssl", 1350 | "openssl-probe", 1351 | "openssl-sys", 1352 | "schannel", 1353 | "security-framework", 1354 | "security-framework-sys", 1355 | "tempfile", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "new_debug_unreachable" 1360 | version = "1.0.6" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 1363 | 1364 | [[package]] 1365 | name = "nu-ansi-term" 1366 | version = "0.46.0" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 1369 | dependencies = [ 1370 | "overload", 1371 | "winapi", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "num-traits" 1376 | version = "0.2.19" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1379 | dependencies = [ 1380 | "autocfg", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "number_prefix" 1385 | version = "0.4.0" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 1388 | 1389 | [[package]] 1390 | name = "object" 1391 | version = "0.36.7" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1394 | dependencies = [ 1395 | "memchr", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "once_cell" 1400 | version = "1.21.1" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" 1403 | 1404 | [[package]] 1405 | name = "openring" 1406 | version = "0.3.7" 1407 | dependencies = [ 1408 | "ammonia", 1409 | "clap", 1410 | "clap-verbosity-flag", 1411 | "csv", 1412 | "dashmap", 1413 | "feed-rs", 1414 | "html-escape", 1415 | "humantime", 1416 | "indicatif", 1417 | "jiff", 1418 | "log", 1419 | "miette", 1420 | "reqwest", 1421 | "serde", 1422 | "serde_json", 1423 | "tera", 1424 | "thiserror 2.0.12", 1425 | "tokio", 1426 | "tracing", 1427 | "tracing-log", 1428 | "tracing-subscriber", 1429 | "url", 1430 | "yansi", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "openssl" 1435 | version = "0.10.71" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" 1438 | dependencies = [ 1439 | "bitflags", 1440 | "cfg-if", 1441 | "foreign-types", 1442 | "libc", 1443 | "once_cell", 1444 | "openssl-macros", 1445 | "openssl-sys", 1446 | ] 1447 | 1448 | [[package]] 1449 | name = "openssl-macros" 1450 | version = "0.1.1" 1451 | source = "registry+https://github.com/rust-lang/crates.io-index" 1452 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1453 | dependencies = [ 1454 | "proc-macro2", 1455 | "quote", 1456 | "syn", 1457 | ] 1458 | 1459 | [[package]] 1460 | name = "openssl-probe" 1461 | version = "0.1.6" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1464 | 1465 | [[package]] 1466 | name = "openssl-sys" 1467 | version = "0.9.106" 1468 | source = "registry+https://github.com/rust-lang/crates.io-index" 1469 | checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" 1470 | dependencies = [ 1471 | "cc", 1472 | "libc", 1473 | "pkg-config", 1474 | "vcpkg", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "overload" 1479 | version = "0.1.1" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1482 | 1483 | [[package]] 1484 | name = "owo-colors" 1485 | version = "4.2.0" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" 1488 | 1489 | [[package]] 1490 | name = "parking_lot" 1491 | version = "0.12.3" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1494 | dependencies = [ 1495 | "lock_api", 1496 | "parking_lot_core", 1497 | ] 1498 | 1499 | [[package]] 1500 | name = "parking_lot_core" 1501 | version = "0.9.10" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1504 | dependencies = [ 1505 | "cfg-if", 1506 | "libc", 1507 | "redox_syscall", 1508 | "smallvec", 1509 | "windows-targets 0.52.6", 1510 | ] 1511 | 1512 | [[package]] 1513 | name = "parse-zoneinfo" 1514 | version = "0.3.1" 1515 | source = "registry+https://github.com/rust-lang/crates.io-index" 1516 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 1517 | dependencies = [ 1518 | "regex", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "percent-encoding" 1523 | version = "2.3.1" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1526 | 1527 | [[package]] 1528 | name = "pest" 1529 | version = "2.7.15" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" 1532 | dependencies = [ 1533 | "memchr", 1534 | "thiserror 2.0.12", 1535 | "ucd-trie", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "pest_derive" 1540 | version = "2.7.15" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" 1543 | dependencies = [ 1544 | "pest", 1545 | "pest_generator", 1546 | ] 1547 | 1548 | [[package]] 1549 | name = "pest_generator" 1550 | version = "2.7.15" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" 1553 | dependencies = [ 1554 | "pest", 1555 | "pest_meta", 1556 | "proc-macro2", 1557 | "quote", 1558 | "syn", 1559 | ] 1560 | 1561 | [[package]] 1562 | name = "pest_meta" 1563 | version = "2.7.15" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" 1566 | dependencies = [ 1567 | "once_cell", 1568 | "pest", 1569 | "sha2", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "phf" 1574 | version = "0.11.3" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 1577 | dependencies = [ 1578 | "phf_shared", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "phf_codegen" 1583 | version = "0.11.3" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 1586 | dependencies = [ 1587 | "phf_generator", 1588 | "phf_shared", 1589 | ] 1590 | 1591 | [[package]] 1592 | name = "phf_generator" 1593 | version = "0.11.3" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 1596 | dependencies = [ 1597 | "phf_shared", 1598 | "rand", 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "phf_shared" 1603 | version = "0.11.3" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 1606 | dependencies = [ 1607 | "siphasher", 1608 | ] 1609 | 1610 | [[package]] 1611 | name = "pin-project-lite" 1612 | version = "0.2.16" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1615 | 1616 | [[package]] 1617 | name = "pin-utils" 1618 | version = "0.1.0" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1621 | 1622 | [[package]] 1623 | name = "pkg-config" 1624 | version = "0.3.32" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1627 | 1628 | [[package]] 1629 | name = "portable-atomic" 1630 | version = "1.11.0" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 1633 | 1634 | [[package]] 1635 | name = "portable-atomic-util" 1636 | version = "0.2.4" 1637 | source = "registry+https://github.com/rust-lang/crates.io-index" 1638 | checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 1639 | dependencies = [ 1640 | "portable-atomic", 1641 | ] 1642 | 1643 | [[package]] 1644 | name = "ppv-lite86" 1645 | version = "0.2.21" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1648 | dependencies = [ 1649 | "zerocopy", 1650 | ] 1651 | 1652 | [[package]] 1653 | name = "precomputed-hash" 1654 | version = "0.1.1" 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" 1656 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 1657 | 1658 | [[package]] 1659 | name = "proc-macro2" 1660 | version = "1.0.94" 1661 | source = "registry+https://github.com/rust-lang/crates.io-index" 1662 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1663 | dependencies = [ 1664 | "unicode-ident", 1665 | ] 1666 | 1667 | [[package]] 1668 | name = "quick-xml" 1669 | version = "0.37.2" 1670 | source = "registry+https://github.com/rust-lang/crates.io-index" 1671 | checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" 1672 | dependencies = [ 1673 | "encoding_rs", 1674 | "memchr", 1675 | ] 1676 | 1677 | [[package]] 1678 | name = "quote" 1679 | version = "1.0.40" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1682 | dependencies = [ 1683 | "proc-macro2", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "r-efi" 1688 | version = "5.2.0" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1691 | 1692 | [[package]] 1693 | name = "rand" 1694 | version = "0.8.5" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1697 | dependencies = [ 1698 | "libc", 1699 | "rand_chacha", 1700 | "rand_core", 1701 | ] 1702 | 1703 | [[package]] 1704 | name = "rand_chacha" 1705 | version = "0.3.1" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1708 | dependencies = [ 1709 | "ppv-lite86", 1710 | "rand_core", 1711 | ] 1712 | 1713 | [[package]] 1714 | name = "rand_core" 1715 | version = "0.6.4" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1718 | dependencies = [ 1719 | "getrandom 0.2.15", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "redox_syscall" 1724 | version = "0.5.10" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" 1727 | dependencies = [ 1728 | "bitflags", 1729 | ] 1730 | 1731 | [[package]] 1732 | name = "regex" 1733 | version = "1.11.1" 1734 | source = "registry+https://github.com/rust-lang/crates.io-index" 1735 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1736 | dependencies = [ 1737 | "aho-corasick", 1738 | "memchr", 1739 | "regex-automata 0.4.9", 1740 | "regex-syntax 0.8.5", 1741 | ] 1742 | 1743 | [[package]] 1744 | name = "regex-automata" 1745 | version = "0.1.10" 1746 | source = "registry+https://github.com/rust-lang/crates.io-index" 1747 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1748 | dependencies = [ 1749 | "regex-syntax 0.6.29", 1750 | ] 1751 | 1752 | [[package]] 1753 | name = "regex-automata" 1754 | version = "0.4.9" 1755 | source = "registry+https://github.com/rust-lang/crates.io-index" 1756 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1757 | dependencies = [ 1758 | "aho-corasick", 1759 | "memchr", 1760 | "regex-syntax 0.8.5", 1761 | ] 1762 | 1763 | [[package]] 1764 | name = "regex-syntax" 1765 | version = "0.6.29" 1766 | source = "registry+https://github.com/rust-lang/crates.io-index" 1767 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1768 | 1769 | [[package]] 1770 | name = "regex-syntax" 1771 | version = "0.8.5" 1772 | source = "registry+https://github.com/rust-lang/crates.io-index" 1773 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1774 | 1775 | [[package]] 1776 | name = "reqwest" 1777 | version = "0.12.15" 1778 | source = "registry+https://github.com/rust-lang/crates.io-index" 1779 | checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 1780 | dependencies = [ 1781 | "async-compression", 1782 | "base64", 1783 | "bytes", 1784 | "encoding_rs", 1785 | "futures-core", 1786 | "futures-util", 1787 | "h2", 1788 | "http", 1789 | "http-body", 1790 | "http-body-util", 1791 | "hyper", 1792 | "hyper-rustls", 1793 | "hyper-tls", 1794 | "hyper-util", 1795 | "ipnet", 1796 | "js-sys", 1797 | "log", 1798 | "mime", 1799 | "native-tls", 1800 | "once_cell", 1801 | "percent-encoding", 1802 | "pin-project-lite", 1803 | "rustls-pemfile", 1804 | "serde", 1805 | "serde_json", 1806 | "serde_urlencoded", 1807 | "sync_wrapper", 1808 | "system-configuration", 1809 | "tokio", 1810 | "tokio-native-tls", 1811 | "tokio-util", 1812 | "tower", 1813 | "tower-service", 1814 | "url", 1815 | "wasm-bindgen", 1816 | "wasm-bindgen-futures", 1817 | "web-sys", 1818 | "windows-registry", 1819 | ] 1820 | 1821 | [[package]] 1822 | name = "ring" 1823 | version = "0.17.14" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1826 | dependencies = [ 1827 | "cc", 1828 | "cfg-if", 1829 | "getrandom 0.2.15", 1830 | "libc", 1831 | "untrusted", 1832 | "windows-sys 0.52.0", 1833 | ] 1834 | 1835 | [[package]] 1836 | name = "rustc-demangle" 1837 | version = "0.1.24" 1838 | source = "registry+https://github.com/rust-lang/crates.io-index" 1839 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1840 | 1841 | [[package]] 1842 | name = "rustix" 1843 | version = "1.0.3" 1844 | source = "registry+https://github.com/rust-lang/crates.io-index" 1845 | checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" 1846 | dependencies = [ 1847 | "bitflags", 1848 | "errno", 1849 | "libc", 1850 | "linux-raw-sys", 1851 | "windows-sys 0.59.0", 1852 | ] 1853 | 1854 | [[package]] 1855 | name = "rustls" 1856 | version = "0.23.25" 1857 | source = "registry+https://github.com/rust-lang/crates.io-index" 1858 | checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" 1859 | dependencies = [ 1860 | "once_cell", 1861 | "rustls-pki-types", 1862 | "rustls-webpki", 1863 | "subtle", 1864 | "zeroize", 1865 | ] 1866 | 1867 | [[package]] 1868 | name = "rustls-pemfile" 1869 | version = "2.2.0" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1872 | dependencies = [ 1873 | "rustls-pki-types", 1874 | ] 1875 | 1876 | [[package]] 1877 | name = "rustls-pki-types" 1878 | version = "1.11.0" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 1881 | 1882 | [[package]] 1883 | name = "rustls-webpki" 1884 | version = "0.103.0" 1885 | source = "registry+https://github.com/rust-lang/crates.io-index" 1886 | checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" 1887 | dependencies = [ 1888 | "ring", 1889 | "rustls-pki-types", 1890 | "untrusted", 1891 | ] 1892 | 1893 | [[package]] 1894 | name = "rustversion" 1895 | version = "1.0.20" 1896 | source = "registry+https://github.com/rust-lang/crates.io-index" 1897 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1898 | 1899 | [[package]] 1900 | name = "ryu" 1901 | version = "1.0.20" 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" 1903 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1904 | 1905 | [[package]] 1906 | name = "same-file" 1907 | version = "1.0.6" 1908 | source = "registry+https://github.com/rust-lang/crates.io-index" 1909 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1910 | dependencies = [ 1911 | "winapi-util", 1912 | ] 1913 | 1914 | [[package]] 1915 | name = "schannel" 1916 | version = "0.1.27" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1919 | dependencies = [ 1920 | "windows-sys 0.59.0", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "scopeguard" 1925 | version = "1.2.0" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1928 | 1929 | [[package]] 1930 | name = "security-framework" 1931 | version = "2.11.1" 1932 | source = "registry+https://github.com/rust-lang/crates.io-index" 1933 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1934 | dependencies = [ 1935 | "bitflags", 1936 | "core-foundation", 1937 | "core-foundation-sys", 1938 | "libc", 1939 | "security-framework-sys", 1940 | ] 1941 | 1942 | [[package]] 1943 | name = "security-framework-sys" 1944 | version = "2.14.0" 1945 | source = "registry+https://github.com/rust-lang/crates.io-index" 1946 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1947 | dependencies = [ 1948 | "core-foundation-sys", 1949 | "libc", 1950 | ] 1951 | 1952 | [[package]] 1953 | name = "serde" 1954 | version = "1.0.219" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1957 | dependencies = [ 1958 | "serde_derive", 1959 | ] 1960 | 1961 | [[package]] 1962 | name = "serde_derive" 1963 | version = "1.0.219" 1964 | source = "registry+https://github.com/rust-lang/crates.io-index" 1965 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1966 | dependencies = [ 1967 | "proc-macro2", 1968 | "quote", 1969 | "syn", 1970 | ] 1971 | 1972 | [[package]] 1973 | name = "serde_json" 1974 | version = "1.0.140" 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" 1976 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1977 | dependencies = [ 1978 | "itoa", 1979 | "memchr", 1980 | "ryu", 1981 | "serde", 1982 | ] 1983 | 1984 | [[package]] 1985 | name = "serde_urlencoded" 1986 | version = "0.7.1" 1987 | source = "registry+https://github.com/rust-lang/crates.io-index" 1988 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1989 | dependencies = [ 1990 | "form_urlencoded", 1991 | "itoa", 1992 | "ryu", 1993 | "serde", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "sha2" 1998 | version = "0.10.8" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 2001 | dependencies = [ 2002 | "cfg-if", 2003 | "cpufeatures", 2004 | "digest", 2005 | ] 2006 | 2007 | [[package]] 2008 | name = "sharded-slab" 2009 | version = "0.1.7" 2010 | source = "registry+https://github.com/rust-lang/crates.io-index" 2011 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 2012 | dependencies = [ 2013 | "lazy_static", 2014 | ] 2015 | 2016 | [[package]] 2017 | name = "shlex" 2018 | version = "1.3.0" 2019 | source = "registry+https://github.com/rust-lang/crates.io-index" 2020 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2021 | 2022 | [[package]] 2023 | name = "signal-hook-registry" 2024 | version = "1.4.2" 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" 2026 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 2027 | dependencies = [ 2028 | "libc", 2029 | ] 2030 | 2031 | [[package]] 2032 | name = "siphasher" 2033 | version = "1.0.1" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 2036 | 2037 | [[package]] 2038 | name = "slab" 2039 | version = "0.4.9" 2040 | source = "registry+https://github.com/rust-lang/crates.io-index" 2041 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2042 | dependencies = [ 2043 | "autocfg", 2044 | ] 2045 | 2046 | [[package]] 2047 | name = "slug" 2048 | version = "0.1.6" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" 2051 | dependencies = [ 2052 | "deunicode", 2053 | "wasm-bindgen", 2054 | ] 2055 | 2056 | [[package]] 2057 | name = "smallvec" 2058 | version = "1.14.0" 2059 | source = "registry+https://github.com/rust-lang/crates.io-index" 2060 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 2061 | 2062 | [[package]] 2063 | name = "socket2" 2064 | version = "0.5.8" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 2067 | dependencies = [ 2068 | "libc", 2069 | "windows-sys 0.52.0", 2070 | ] 2071 | 2072 | [[package]] 2073 | name = "stable_deref_trait" 2074 | version = "1.2.0" 2075 | source = "registry+https://github.com/rust-lang/crates.io-index" 2076 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2077 | 2078 | [[package]] 2079 | name = "string_cache" 2080 | version = "0.8.8" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" 2083 | dependencies = [ 2084 | "new_debug_unreachable", 2085 | "parking_lot", 2086 | "phf_shared", 2087 | "precomputed-hash", 2088 | "serde", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "string_cache_codegen" 2093 | version = "0.5.4" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" 2096 | dependencies = [ 2097 | "phf_generator", 2098 | "phf_shared", 2099 | "proc-macro2", 2100 | "quote", 2101 | ] 2102 | 2103 | [[package]] 2104 | name = "strsim" 2105 | version = "0.11.1" 2106 | source = "registry+https://github.com/rust-lang/crates.io-index" 2107 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2108 | 2109 | [[package]] 2110 | name = "subtle" 2111 | version = "2.6.1" 2112 | source = "registry+https://github.com/rust-lang/crates.io-index" 2113 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2114 | 2115 | [[package]] 2116 | name = "supports-color" 2117 | version = "3.0.2" 2118 | source = "registry+https://github.com/rust-lang/crates.io-index" 2119 | checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" 2120 | dependencies = [ 2121 | "is_ci", 2122 | ] 2123 | 2124 | [[package]] 2125 | name = "supports-hyperlinks" 2126 | version = "3.1.0" 2127 | source = "registry+https://github.com/rust-lang/crates.io-index" 2128 | checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" 2129 | 2130 | [[package]] 2131 | name = "supports-unicode" 2132 | version = "3.0.0" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 2135 | 2136 | [[package]] 2137 | name = "syn" 2138 | version = "2.0.100" 2139 | source = "registry+https://github.com/rust-lang/crates.io-index" 2140 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 2141 | dependencies = [ 2142 | "proc-macro2", 2143 | "quote", 2144 | "unicode-ident", 2145 | ] 2146 | 2147 | [[package]] 2148 | name = "sync_wrapper" 2149 | version = "1.0.2" 2150 | source = "registry+https://github.com/rust-lang/crates.io-index" 2151 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 2152 | dependencies = [ 2153 | "futures-core", 2154 | ] 2155 | 2156 | [[package]] 2157 | name = "synstructure" 2158 | version = "0.13.1" 2159 | source = "registry+https://github.com/rust-lang/crates.io-index" 2160 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 2161 | dependencies = [ 2162 | "proc-macro2", 2163 | "quote", 2164 | "syn", 2165 | ] 2166 | 2167 | [[package]] 2168 | name = "system-configuration" 2169 | version = "0.6.1" 2170 | source = "registry+https://github.com/rust-lang/crates.io-index" 2171 | checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 2172 | dependencies = [ 2173 | "bitflags", 2174 | "core-foundation", 2175 | "system-configuration-sys", 2176 | ] 2177 | 2178 | [[package]] 2179 | name = "system-configuration-sys" 2180 | version = "0.6.0" 2181 | source = "registry+https://github.com/rust-lang/crates.io-index" 2182 | checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 2183 | dependencies = [ 2184 | "core-foundation-sys", 2185 | "libc", 2186 | ] 2187 | 2188 | [[package]] 2189 | name = "tempfile" 2190 | version = "3.19.1" 2191 | source = "registry+https://github.com/rust-lang/crates.io-index" 2192 | checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" 2193 | dependencies = [ 2194 | "fastrand", 2195 | "getrandom 0.3.2", 2196 | "once_cell", 2197 | "rustix", 2198 | "windows-sys 0.59.0", 2199 | ] 2200 | 2201 | [[package]] 2202 | name = "tendril" 2203 | version = "0.4.3" 2204 | source = "registry+https://github.com/rust-lang/crates.io-index" 2205 | checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" 2206 | dependencies = [ 2207 | "futf", 2208 | "mac", 2209 | "utf-8", 2210 | ] 2211 | 2212 | [[package]] 2213 | name = "tera" 2214 | version = "1.20.0" 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" 2216 | checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" 2217 | dependencies = [ 2218 | "chrono", 2219 | "chrono-tz", 2220 | "globwalk", 2221 | "humansize", 2222 | "lazy_static", 2223 | "percent-encoding", 2224 | "pest", 2225 | "pest_derive", 2226 | "rand", 2227 | "regex", 2228 | "serde", 2229 | "serde_json", 2230 | "slug", 2231 | "unic-segment", 2232 | ] 2233 | 2234 | [[package]] 2235 | name = "terminal_size" 2236 | version = "0.4.2" 2237 | source = "registry+https://github.com/rust-lang/crates.io-index" 2238 | checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" 2239 | dependencies = [ 2240 | "rustix", 2241 | "windows-sys 0.59.0", 2242 | ] 2243 | 2244 | [[package]] 2245 | name = "textwrap" 2246 | version = "0.16.2" 2247 | source = "registry+https://github.com/rust-lang/crates.io-index" 2248 | checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" 2249 | dependencies = [ 2250 | "unicode-linebreak", 2251 | "unicode-width 0.2.0", 2252 | ] 2253 | 2254 | [[package]] 2255 | name = "thiserror" 2256 | version = "1.0.69" 2257 | source = "registry+https://github.com/rust-lang/crates.io-index" 2258 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 2259 | dependencies = [ 2260 | "thiserror-impl 1.0.69", 2261 | ] 2262 | 2263 | [[package]] 2264 | name = "thiserror" 2265 | version = "2.0.12" 2266 | source = "registry+https://github.com/rust-lang/crates.io-index" 2267 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 2268 | dependencies = [ 2269 | "thiserror-impl 2.0.12", 2270 | ] 2271 | 2272 | [[package]] 2273 | name = "thiserror-impl" 2274 | version = "1.0.69" 2275 | source = "registry+https://github.com/rust-lang/crates.io-index" 2276 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 2277 | dependencies = [ 2278 | "proc-macro2", 2279 | "quote", 2280 | "syn", 2281 | ] 2282 | 2283 | [[package]] 2284 | name = "thiserror-impl" 2285 | version = "2.0.12" 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" 2287 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 2288 | dependencies = [ 2289 | "proc-macro2", 2290 | "quote", 2291 | "syn", 2292 | ] 2293 | 2294 | [[package]] 2295 | name = "thread_local" 2296 | version = "1.1.8" 2297 | source = "registry+https://github.com/rust-lang/crates.io-index" 2298 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 2299 | dependencies = [ 2300 | "cfg-if", 2301 | "once_cell", 2302 | ] 2303 | 2304 | [[package]] 2305 | name = "tinystr" 2306 | version = "0.7.6" 2307 | source = "registry+https://github.com/rust-lang/crates.io-index" 2308 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 2309 | dependencies = [ 2310 | "displaydoc", 2311 | "zerovec", 2312 | ] 2313 | 2314 | [[package]] 2315 | name = "tokio" 2316 | version = "1.44.1" 2317 | source = "registry+https://github.com/rust-lang/crates.io-index" 2318 | checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" 2319 | dependencies = [ 2320 | "backtrace", 2321 | "bytes", 2322 | "libc", 2323 | "mio", 2324 | "parking_lot", 2325 | "pin-project-lite", 2326 | "signal-hook-registry", 2327 | "socket2", 2328 | "tokio-macros", 2329 | "windows-sys 0.52.0", 2330 | ] 2331 | 2332 | [[package]] 2333 | name = "tokio-macros" 2334 | version = "2.5.0" 2335 | source = "registry+https://github.com/rust-lang/crates.io-index" 2336 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 2337 | dependencies = [ 2338 | "proc-macro2", 2339 | "quote", 2340 | "syn", 2341 | ] 2342 | 2343 | [[package]] 2344 | name = "tokio-native-tls" 2345 | version = "0.3.1" 2346 | source = "registry+https://github.com/rust-lang/crates.io-index" 2347 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2348 | dependencies = [ 2349 | "native-tls", 2350 | "tokio", 2351 | ] 2352 | 2353 | [[package]] 2354 | name = "tokio-rustls" 2355 | version = "0.26.2" 2356 | source = "registry+https://github.com/rust-lang/crates.io-index" 2357 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 2358 | dependencies = [ 2359 | "rustls", 2360 | "tokio", 2361 | ] 2362 | 2363 | [[package]] 2364 | name = "tokio-util" 2365 | version = "0.7.14" 2366 | source = "registry+https://github.com/rust-lang/crates.io-index" 2367 | checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" 2368 | dependencies = [ 2369 | "bytes", 2370 | "futures-core", 2371 | "futures-sink", 2372 | "pin-project-lite", 2373 | "tokio", 2374 | ] 2375 | 2376 | [[package]] 2377 | name = "tower" 2378 | version = "0.5.2" 2379 | source = "registry+https://github.com/rust-lang/crates.io-index" 2380 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 2381 | dependencies = [ 2382 | "futures-core", 2383 | "futures-util", 2384 | "pin-project-lite", 2385 | "sync_wrapper", 2386 | "tokio", 2387 | "tower-layer", 2388 | "tower-service", 2389 | ] 2390 | 2391 | [[package]] 2392 | name = "tower-layer" 2393 | version = "0.3.3" 2394 | source = "registry+https://github.com/rust-lang/crates.io-index" 2395 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2396 | 2397 | [[package]] 2398 | name = "tower-service" 2399 | version = "0.3.3" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2402 | 2403 | [[package]] 2404 | name = "tracing" 2405 | version = "0.1.41" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 2408 | dependencies = [ 2409 | "pin-project-lite", 2410 | "tracing-attributes", 2411 | "tracing-core", 2412 | ] 2413 | 2414 | [[package]] 2415 | name = "tracing-attributes" 2416 | version = "0.1.28" 2417 | source = "registry+https://github.com/rust-lang/crates.io-index" 2418 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 2419 | dependencies = [ 2420 | "proc-macro2", 2421 | "quote", 2422 | "syn", 2423 | ] 2424 | 2425 | [[package]] 2426 | name = "tracing-core" 2427 | version = "0.1.33" 2428 | source = "registry+https://github.com/rust-lang/crates.io-index" 2429 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 2430 | dependencies = [ 2431 | "once_cell", 2432 | "valuable", 2433 | ] 2434 | 2435 | [[package]] 2436 | name = "tracing-log" 2437 | version = "0.2.0" 2438 | source = "registry+https://github.com/rust-lang/crates.io-index" 2439 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 2440 | dependencies = [ 2441 | "log", 2442 | "once_cell", 2443 | "tracing-core", 2444 | ] 2445 | 2446 | [[package]] 2447 | name = "tracing-subscriber" 2448 | version = "0.3.19" 2449 | source = "registry+https://github.com/rust-lang/crates.io-index" 2450 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 2451 | dependencies = [ 2452 | "matchers", 2453 | "nu-ansi-term", 2454 | "once_cell", 2455 | "regex", 2456 | "sharded-slab", 2457 | "smallvec", 2458 | "thread_local", 2459 | "tracing", 2460 | "tracing-core", 2461 | "tracing-log", 2462 | ] 2463 | 2464 | [[package]] 2465 | name = "try-lock" 2466 | version = "0.2.5" 2467 | source = "registry+https://github.com/rust-lang/crates.io-index" 2468 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2469 | 2470 | [[package]] 2471 | name = "typenum" 2472 | version = "1.18.0" 2473 | source = "registry+https://github.com/rust-lang/crates.io-index" 2474 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2475 | 2476 | [[package]] 2477 | name = "ucd-trie" 2478 | version = "0.1.7" 2479 | source = "registry+https://github.com/rust-lang/crates.io-index" 2480 | checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 2481 | 2482 | [[package]] 2483 | name = "unic-char-property" 2484 | version = "0.9.0" 2485 | source = "registry+https://github.com/rust-lang/crates.io-index" 2486 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 2487 | dependencies = [ 2488 | "unic-char-range", 2489 | ] 2490 | 2491 | [[package]] 2492 | name = "unic-char-range" 2493 | version = "0.9.0" 2494 | source = "registry+https://github.com/rust-lang/crates.io-index" 2495 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 2496 | 2497 | [[package]] 2498 | name = "unic-common" 2499 | version = "0.9.0" 2500 | source = "registry+https://github.com/rust-lang/crates.io-index" 2501 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 2502 | 2503 | [[package]] 2504 | name = "unic-segment" 2505 | version = "0.9.0" 2506 | source = "registry+https://github.com/rust-lang/crates.io-index" 2507 | checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" 2508 | dependencies = [ 2509 | "unic-ucd-segment", 2510 | ] 2511 | 2512 | [[package]] 2513 | name = "unic-ucd-segment" 2514 | version = "0.9.0" 2515 | source = "registry+https://github.com/rust-lang/crates.io-index" 2516 | checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" 2517 | dependencies = [ 2518 | "unic-char-property", 2519 | "unic-char-range", 2520 | "unic-ucd-version", 2521 | ] 2522 | 2523 | [[package]] 2524 | name = "unic-ucd-version" 2525 | version = "0.9.0" 2526 | source = "registry+https://github.com/rust-lang/crates.io-index" 2527 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 2528 | dependencies = [ 2529 | "unic-common", 2530 | ] 2531 | 2532 | [[package]] 2533 | name = "unicode-ident" 2534 | version = "1.0.18" 2535 | source = "registry+https://github.com/rust-lang/crates.io-index" 2536 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 2537 | 2538 | [[package]] 2539 | name = "unicode-linebreak" 2540 | version = "0.1.5" 2541 | source = "registry+https://github.com/rust-lang/crates.io-index" 2542 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 2543 | 2544 | [[package]] 2545 | name = "unicode-width" 2546 | version = "0.1.14" 2547 | source = "registry+https://github.com/rust-lang/crates.io-index" 2548 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2549 | 2550 | [[package]] 2551 | name = "unicode-width" 2552 | version = "0.2.0" 2553 | source = "registry+https://github.com/rust-lang/crates.io-index" 2554 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 2555 | 2556 | [[package]] 2557 | name = "untrusted" 2558 | version = "0.9.0" 2559 | source = "registry+https://github.com/rust-lang/crates.io-index" 2560 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2561 | 2562 | [[package]] 2563 | name = "url" 2564 | version = "2.5.4" 2565 | source = "registry+https://github.com/rust-lang/crates.io-index" 2566 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 2567 | dependencies = [ 2568 | "form_urlencoded", 2569 | "idna", 2570 | "percent-encoding", 2571 | "serde", 2572 | ] 2573 | 2574 | [[package]] 2575 | name = "utf-8" 2576 | version = "0.7.6" 2577 | source = "registry+https://github.com/rust-lang/crates.io-index" 2578 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 2579 | 2580 | [[package]] 2581 | name = "utf16_iter" 2582 | version = "1.0.5" 2583 | source = "registry+https://github.com/rust-lang/crates.io-index" 2584 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 2585 | 2586 | [[package]] 2587 | name = "utf8-width" 2588 | version = "0.1.7" 2589 | source = "registry+https://github.com/rust-lang/crates.io-index" 2590 | checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" 2591 | 2592 | [[package]] 2593 | name = "utf8_iter" 2594 | version = "1.0.4" 2595 | source = "registry+https://github.com/rust-lang/crates.io-index" 2596 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2597 | 2598 | [[package]] 2599 | name = "utf8parse" 2600 | version = "0.2.2" 2601 | source = "registry+https://github.com/rust-lang/crates.io-index" 2602 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2603 | 2604 | [[package]] 2605 | name = "uuid" 2606 | version = "1.16.0" 2607 | source = "registry+https://github.com/rust-lang/crates.io-index" 2608 | checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" 2609 | dependencies = [ 2610 | "getrandom 0.3.2", 2611 | ] 2612 | 2613 | [[package]] 2614 | name = "valuable" 2615 | version = "0.1.1" 2616 | source = "registry+https://github.com/rust-lang/crates.io-index" 2617 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2618 | 2619 | [[package]] 2620 | name = "vcpkg" 2621 | version = "0.2.15" 2622 | source = "registry+https://github.com/rust-lang/crates.io-index" 2623 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2624 | 2625 | [[package]] 2626 | name = "version_check" 2627 | version = "0.9.5" 2628 | source = "registry+https://github.com/rust-lang/crates.io-index" 2629 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2630 | 2631 | [[package]] 2632 | name = "walkdir" 2633 | version = "2.5.0" 2634 | source = "registry+https://github.com/rust-lang/crates.io-index" 2635 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2636 | dependencies = [ 2637 | "same-file", 2638 | "winapi-util", 2639 | ] 2640 | 2641 | [[package]] 2642 | name = "want" 2643 | version = "0.3.1" 2644 | source = "registry+https://github.com/rust-lang/crates.io-index" 2645 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2646 | dependencies = [ 2647 | "try-lock", 2648 | ] 2649 | 2650 | [[package]] 2651 | name = "wasi" 2652 | version = "0.11.0+wasi-snapshot-preview1" 2653 | source = "registry+https://github.com/rust-lang/crates.io-index" 2654 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2655 | 2656 | [[package]] 2657 | name = "wasi" 2658 | version = "0.14.2+wasi-0.2.4" 2659 | source = "registry+https://github.com/rust-lang/crates.io-index" 2660 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 2661 | dependencies = [ 2662 | "wit-bindgen-rt", 2663 | ] 2664 | 2665 | [[package]] 2666 | name = "wasm-bindgen" 2667 | version = "0.2.100" 2668 | source = "registry+https://github.com/rust-lang/crates.io-index" 2669 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 2670 | dependencies = [ 2671 | "cfg-if", 2672 | "once_cell", 2673 | "rustversion", 2674 | "wasm-bindgen-macro", 2675 | ] 2676 | 2677 | [[package]] 2678 | name = "wasm-bindgen-backend" 2679 | version = "0.2.100" 2680 | source = "registry+https://github.com/rust-lang/crates.io-index" 2681 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 2682 | dependencies = [ 2683 | "bumpalo", 2684 | "log", 2685 | "proc-macro2", 2686 | "quote", 2687 | "syn", 2688 | "wasm-bindgen-shared", 2689 | ] 2690 | 2691 | [[package]] 2692 | name = "wasm-bindgen-futures" 2693 | version = "0.4.50" 2694 | source = "registry+https://github.com/rust-lang/crates.io-index" 2695 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 2696 | dependencies = [ 2697 | "cfg-if", 2698 | "js-sys", 2699 | "once_cell", 2700 | "wasm-bindgen", 2701 | "web-sys", 2702 | ] 2703 | 2704 | [[package]] 2705 | name = "wasm-bindgen-macro" 2706 | version = "0.2.100" 2707 | source = "registry+https://github.com/rust-lang/crates.io-index" 2708 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 2709 | dependencies = [ 2710 | "quote", 2711 | "wasm-bindgen-macro-support", 2712 | ] 2713 | 2714 | [[package]] 2715 | name = "wasm-bindgen-macro-support" 2716 | version = "0.2.100" 2717 | source = "registry+https://github.com/rust-lang/crates.io-index" 2718 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 2719 | dependencies = [ 2720 | "proc-macro2", 2721 | "quote", 2722 | "syn", 2723 | "wasm-bindgen-backend", 2724 | "wasm-bindgen-shared", 2725 | ] 2726 | 2727 | [[package]] 2728 | name = "wasm-bindgen-shared" 2729 | version = "0.2.100" 2730 | source = "registry+https://github.com/rust-lang/crates.io-index" 2731 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 2732 | dependencies = [ 2733 | "unicode-ident", 2734 | ] 2735 | 2736 | [[package]] 2737 | name = "web-sys" 2738 | version = "0.3.77" 2739 | source = "registry+https://github.com/rust-lang/crates.io-index" 2740 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 2741 | dependencies = [ 2742 | "js-sys", 2743 | "wasm-bindgen", 2744 | ] 2745 | 2746 | [[package]] 2747 | name = "web-time" 2748 | version = "1.1.0" 2749 | source = "registry+https://github.com/rust-lang/crates.io-index" 2750 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2751 | dependencies = [ 2752 | "js-sys", 2753 | "wasm-bindgen", 2754 | ] 2755 | 2756 | [[package]] 2757 | name = "winapi" 2758 | version = "0.3.9" 2759 | source = "registry+https://github.com/rust-lang/crates.io-index" 2760 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2761 | dependencies = [ 2762 | "winapi-i686-pc-windows-gnu", 2763 | "winapi-x86_64-pc-windows-gnu", 2764 | ] 2765 | 2766 | [[package]] 2767 | name = "winapi-i686-pc-windows-gnu" 2768 | version = "0.4.0" 2769 | source = "registry+https://github.com/rust-lang/crates.io-index" 2770 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2771 | 2772 | [[package]] 2773 | name = "winapi-util" 2774 | version = "0.1.9" 2775 | source = "registry+https://github.com/rust-lang/crates.io-index" 2776 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 2777 | dependencies = [ 2778 | "windows-sys 0.59.0", 2779 | ] 2780 | 2781 | [[package]] 2782 | name = "winapi-x86_64-pc-windows-gnu" 2783 | version = "0.4.0" 2784 | source = "registry+https://github.com/rust-lang/crates.io-index" 2785 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2786 | 2787 | [[package]] 2788 | name = "windows-core" 2789 | version = "0.52.0" 2790 | source = "registry+https://github.com/rust-lang/crates.io-index" 2791 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2792 | dependencies = [ 2793 | "windows-targets 0.52.6", 2794 | ] 2795 | 2796 | [[package]] 2797 | name = "windows-link" 2798 | version = "0.1.1" 2799 | source = "registry+https://github.com/rust-lang/crates.io-index" 2800 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 2801 | 2802 | [[package]] 2803 | name = "windows-registry" 2804 | version = "0.4.0" 2805 | source = "registry+https://github.com/rust-lang/crates.io-index" 2806 | checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 2807 | dependencies = [ 2808 | "windows-result", 2809 | "windows-strings", 2810 | "windows-targets 0.53.0", 2811 | ] 2812 | 2813 | [[package]] 2814 | name = "windows-result" 2815 | version = "0.3.2" 2816 | source = "registry+https://github.com/rust-lang/crates.io-index" 2817 | checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" 2818 | dependencies = [ 2819 | "windows-link", 2820 | ] 2821 | 2822 | [[package]] 2823 | name = "windows-strings" 2824 | version = "0.3.1" 2825 | source = "registry+https://github.com/rust-lang/crates.io-index" 2826 | checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 2827 | dependencies = [ 2828 | "windows-link", 2829 | ] 2830 | 2831 | [[package]] 2832 | name = "windows-sys" 2833 | version = "0.52.0" 2834 | source = "registry+https://github.com/rust-lang/crates.io-index" 2835 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2836 | dependencies = [ 2837 | "windows-targets 0.52.6", 2838 | ] 2839 | 2840 | [[package]] 2841 | name = "windows-sys" 2842 | version = "0.59.0" 2843 | source = "registry+https://github.com/rust-lang/crates.io-index" 2844 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2845 | dependencies = [ 2846 | "windows-targets 0.52.6", 2847 | ] 2848 | 2849 | [[package]] 2850 | name = "windows-targets" 2851 | version = "0.52.6" 2852 | source = "registry+https://github.com/rust-lang/crates.io-index" 2853 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2854 | dependencies = [ 2855 | "windows_aarch64_gnullvm 0.52.6", 2856 | "windows_aarch64_msvc 0.52.6", 2857 | "windows_i686_gnu 0.52.6", 2858 | "windows_i686_gnullvm 0.52.6", 2859 | "windows_i686_msvc 0.52.6", 2860 | "windows_x86_64_gnu 0.52.6", 2861 | "windows_x86_64_gnullvm 0.52.6", 2862 | "windows_x86_64_msvc 0.52.6", 2863 | ] 2864 | 2865 | [[package]] 2866 | name = "windows-targets" 2867 | version = "0.53.0" 2868 | source = "registry+https://github.com/rust-lang/crates.io-index" 2869 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 2870 | dependencies = [ 2871 | "windows_aarch64_gnullvm 0.53.0", 2872 | "windows_aarch64_msvc 0.53.0", 2873 | "windows_i686_gnu 0.53.0", 2874 | "windows_i686_gnullvm 0.53.0", 2875 | "windows_i686_msvc 0.53.0", 2876 | "windows_x86_64_gnu 0.53.0", 2877 | "windows_x86_64_gnullvm 0.53.0", 2878 | "windows_x86_64_msvc 0.53.0", 2879 | ] 2880 | 2881 | [[package]] 2882 | name = "windows_aarch64_gnullvm" 2883 | version = "0.52.6" 2884 | source = "registry+https://github.com/rust-lang/crates.io-index" 2885 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2886 | 2887 | [[package]] 2888 | name = "windows_aarch64_gnullvm" 2889 | version = "0.53.0" 2890 | source = "registry+https://github.com/rust-lang/crates.io-index" 2891 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2892 | 2893 | [[package]] 2894 | name = "windows_aarch64_msvc" 2895 | version = "0.52.6" 2896 | source = "registry+https://github.com/rust-lang/crates.io-index" 2897 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2898 | 2899 | [[package]] 2900 | name = "windows_aarch64_msvc" 2901 | version = "0.53.0" 2902 | source = "registry+https://github.com/rust-lang/crates.io-index" 2903 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2904 | 2905 | [[package]] 2906 | name = "windows_i686_gnu" 2907 | version = "0.52.6" 2908 | source = "registry+https://github.com/rust-lang/crates.io-index" 2909 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2910 | 2911 | [[package]] 2912 | name = "windows_i686_gnu" 2913 | version = "0.53.0" 2914 | source = "registry+https://github.com/rust-lang/crates.io-index" 2915 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2916 | 2917 | [[package]] 2918 | name = "windows_i686_gnullvm" 2919 | version = "0.52.6" 2920 | source = "registry+https://github.com/rust-lang/crates.io-index" 2921 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2922 | 2923 | [[package]] 2924 | name = "windows_i686_gnullvm" 2925 | version = "0.53.0" 2926 | source = "registry+https://github.com/rust-lang/crates.io-index" 2927 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2928 | 2929 | [[package]] 2930 | name = "windows_i686_msvc" 2931 | version = "0.52.6" 2932 | source = "registry+https://github.com/rust-lang/crates.io-index" 2933 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2934 | 2935 | [[package]] 2936 | name = "windows_i686_msvc" 2937 | version = "0.53.0" 2938 | source = "registry+https://github.com/rust-lang/crates.io-index" 2939 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2940 | 2941 | [[package]] 2942 | name = "windows_x86_64_gnu" 2943 | version = "0.52.6" 2944 | source = "registry+https://github.com/rust-lang/crates.io-index" 2945 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2946 | 2947 | [[package]] 2948 | name = "windows_x86_64_gnu" 2949 | version = "0.53.0" 2950 | source = "registry+https://github.com/rust-lang/crates.io-index" 2951 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2952 | 2953 | [[package]] 2954 | name = "windows_x86_64_gnullvm" 2955 | version = "0.52.6" 2956 | source = "registry+https://github.com/rust-lang/crates.io-index" 2957 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2958 | 2959 | [[package]] 2960 | name = "windows_x86_64_gnullvm" 2961 | version = "0.53.0" 2962 | source = "registry+https://github.com/rust-lang/crates.io-index" 2963 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2964 | 2965 | [[package]] 2966 | name = "windows_x86_64_msvc" 2967 | version = "0.52.6" 2968 | source = "registry+https://github.com/rust-lang/crates.io-index" 2969 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2970 | 2971 | [[package]] 2972 | name = "windows_x86_64_msvc" 2973 | version = "0.53.0" 2974 | source = "registry+https://github.com/rust-lang/crates.io-index" 2975 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2976 | 2977 | [[package]] 2978 | name = "wit-bindgen-rt" 2979 | version = "0.39.0" 2980 | source = "registry+https://github.com/rust-lang/crates.io-index" 2981 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2982 | dependencies = [ 2983 | "bitflags", 2984 | ] 2985 | 2986 | [[package]] 2987 | name = "write16" 2988 | version = "1.0.0" 2989 | source = "registry+https://github.com/rust-lang/crates.io-index" 2990 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2991 | 2992 | [[package]] 2993 | name = "writeable" 2994 | version = "0.5.5" 2995 | source = "registry+https://github.com/rust-lang/crates.io-index" 2996 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2997 | 2998 | [[package]] 2999 | name = "yansi" 3000 | version = "1.0.1" 3001 | source = "registry+https://github.com/rust-lang/crates.io-index" 3002 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 3003 | dependencies = [ 3004 | "is-terminal", 3005 | ] 3006 | 3007 | [[package]] 3008 | name = "yoke" 3009 | version = "0.7.5" 3010 | source = "registry+https://github.com/rust-lang/crates.io-index" 3011 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 3012 | dependencies = [ 3013 | "serde", 3014 | "stable_deref_trait", 3015 | "yoke-derive", 3016 | "zerofrom", 3017 | ] 3018 | 3019 | [[package]] 3020 | name = "yoke-derive" 3021 | version = "0.7.5" 3022 | source = "registry+https://github.com/rust-lang/crates.io-index" 3023 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 3024 | dependencies = [ 3025 | "proc-macro2", 3026 | "quote", 3027 | "syn", 3028 | "synstructure", 3029 | ] 3030 | 3031 | [[package]] 3032 | name = "zerocopy" 3033 | version = "0.8.24" 3034 | source = "registry+https://github.com/rust-lang/crates.io-index" 3035 | checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" 3036 | dependencies = [ 3037 | "zerocopy-derive", 3038 | ] 3039 | 3040 | [[package]] 3041 | name = "zerocopy-derive" 3042 | version = "0.8.24" 3043 | source = "registry+https://github.com/rust-lang/crates.io-index" 3044 | checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" 3045 | dependencies = [ 3046 | "proc-macro2", 3047 | "quote", 3048 | "syn", 3049 | ] 3050 | 3051 | [[package]] 3052 | name = "zerofrom" 3053 | version = "0.1.6" 3054 | source = "registry+https://github.com/rust-lang/crates.io-index" 3055 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 3056 | dependencies = [ 3057 | "zerofrom-derive", 3058 | ] 3059 | 3060 | [[package]] 3061 | name = "zerofrom-derive" 3062 | version = "0.1.6" 3063 | source = "registry+https://github.com/rust-lang/crates.io-index" 3064 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 3065 | dependencies = [ 3066 | "proc-macro2", 3067 | "quote", 3068 | "syn", 3069 | "synstructure", 3070 | ] 3071 | 3072 | [[package]] 3073 | name = "zeroize" 3074 | version = "1.8.1" 3075 | source = "registry+https://github.com/rust-lang/crates.io-index" 3076 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 3077 | 3078 | [[package]] 3079 | name = "zerovec" 3080 | version = "0.10.4" 3081 | source = "registry+https://github.com/rust-lang/crates.io-index" 3082 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 3083 | dependencies = [ 3084 | "yoke", 3085 | "zerofrom", 3086 | "zerovec-derive", 3087 | ] 3088 | 3089 | [[package]] 3090 | name = "zerovec-derive" 3091 | version = "0.10.3" 3092 | source = "registry+https://github.com/rust-lang/crates.io-index" 3093 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 3094 | dependencies = [ 3095 | "proc-macro2", 3096 | "quote", 3097 | "syn", 3098 | ] 3099 | 3100 | [[package]] 3101 | name = "zstd" 3102 | version = "0.13.3" 3103 | source = "registry+https://github.com/rust-lang/crates.io-index" 3104 | checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 3105 | dependencies = [ 3106 | "zstd-safe", 3107 | ] 3108 | 3109 | [[package]] 3110 | name = "zstd-safe" 3111 | version = "7.2.4" 3112 | source = "registry+https://github.com/rust-lang/crates.io-index" 3113 | checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 3114 | dependencies = [ 3115 | "zstd-sys", 3116 | ] 3117 | 3118 | [[package]] 3119 | name = "zstd-sys" 3120 | version = "2.0.15+zstd.1.5.7" 3121 | source = "registry+https://github.com/rust-lang/crates.io-index" 3122 | checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 3123 | dependencies = [ 3124 | "cc", 3125 | "pkg-config", 3126 | ] 3127 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openring" 3 | version = "0.3.7" 4 | edition = "2024" 5 | authors = ["Luke Hsiao "] 6 | description = "A webring for static site generators written in Rust" 7 | repository = "https://github.com/lukehsiao/openring-rs" 8 | homepage = "https://github.com/lukehsiao/openring-rs" 9 | license = "BlueOak-1.0.0" 10 | keywords = ["webring"] 11 | categories = ["command-line-utilities"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ammonia = "4.0.0" 17 | clap = { version = "4.5.32", features = ["derive", "wrap_help", "cargo"] } 18 | clap-verbosity-flag = "3.0.2" 19 | csv = "1.3.1" 20 | dashmap = "6.1.0" 21 | feed-rs = "2.3.1" 22 | html-escape = "0.2.13" 23 | humantime = "2.2.0" 24 | indicatif = "0.17.11" 25 | jiff = { version = "0.2.4", features = ["serde"] } 26 | log = "0.4.26" 27 | miette = { version = "7.5.0", features = ["fancy"] } 28 | reqwest = { version = "0.12.15", features = ["gzip", "brotli", "zstd", "deflate"] } 29 | serde = { version = "1.0.219", features = ["derive"] } 30 | serde_json = "1.0.140" 31 | tera = "1.20.0" 32 | thiserror = "2.0.12" 33 | tokio = { version = "1.44.1", features = ["full"] } 34 | tracing = "0.1.41" 35 | tracing-log = "0.2.0" 36 | tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt"] } 37 | url = { version = "2.5.4", features = ["serde"] } 38 | yansi = { version = "1.0.1", features = ["detect-tty", "detect-env"] } 39 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | # just manual: https://github.com/casey/just 2 | 3 | _default: 4 | @just --list 5 | 6 | # Runs clippy on the sources 7 | check: 8 | cargo clippy --all-targets --all-features --locked -- -W clippy::pedantic -D warnings 9 | 10 | # check security advisories 11 | audit: 12 | cargo deny check advisories 13 | 14 | # Check links in markdown files 15 | link-check: 16 | -lychee -E '**/*.md' 17 | 18 | # Format source 19 | fmt: 20 | cargo fmt 21 | 22 | # Sets up a watcher that lints, tests, and builds 23 | watch: 24 | bacon 25 | 26 | # Update the changelog using git-cliff 27 | _update_changelog version: 28 | #!/usr/bin/env bash 29 | set -euxo pipefail 30 | 31 | # Update changelog 32 | if ! command -v git-cliff &> /dev/null 33 | then 34 | echo "Please install git-cliff: https://github.com/orhun/git-cliff#installation" 35 | exit 36 | fi 37 | 38 | git-cliff --tag {{version}} --unreleased --prepend CHANGELOG.md 39 | ${EDITOR:-vi} CHANGELOG.md 40 | git commit CHANGELOG.md -m "docs(CHANGELOG): add entry for {{version}}" 41 | 42 | # Increment the version 43 | _incr_version version: (_update_changelog version) 44 | #!/usr/bin/env bash 45 | set -euxo pipefail 46 | 47 | # Update version 48 | cargo set-version {{trim_start_match(version, "v")}} 49 | cargo build --release 50 | git commit Cargo.toml Cargo.lock -m "chore(release): bump version to {{version}}" 51 | 52 | # Get the changelog and git stats for the release 53 | _tlog describe version: 54 | # Format git-cliff output friendly for the tag 55 | @git cliff -c minimal --strip all --unreleased --tag {{version}} | sd "(^## .*\n\s+|^See the.*|^\[.*|^\s*$|^###\s)" "" 56 | @echo "$ git stats -r {{describe}}..{{version}}" 57 | @git stats -r {{describe}}..HEAD 58 | 59 | # Target can be ["major", "minor", "patch", or a version] 60 | release target: 61 | #!/usr/bin/env python3 62 | # Inspired-by: https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver 63 | import os 64 | import subprocess 65 | import sys 66 | import tempfile 67 | 68 | if subprocess.run(["git", "branch", "--show-current"], stdout=subprocess.PIPE 69 | ).stdout.decode().strip() != "main": 70 | print("WARNING! Not on the main branch.") 71 | 72 | subprocess.run(["git", "pull", "--rebase"]) 73 | p = subprocess.run(["git", "describe", "--abbrev=0"], stdout=subprocess.PIPE) 74 | describe = p.stdout.decode().strip() 75 | old_version = describe[1:].split("-")[0].split(".") 76 | if len(old_version) == 2: 77 | [major, minor] = old_version 78 | [major, minor] = map(int, [major, minor]) 79 | patch = 0 80 | else: 81 | [major, minor, patch] = old_version 82 | [major, minor, patch] = map(int, [major, minor, patch]) 83 | 84 | new_version = None 85 | 86 | if "{{target}}" == "patch": 87 | patch += 1 88 | elif "{{target}}" == "minor": 89 | minor += 1 90 | patch = 0 91 | elif "{{target}}" == "major": 92 | major += 1 93 | minor = patch = 0 94 | else: 95 | new_version = "{{target}}" 96 | 97 | if new_version is None: 98 | if len(old_version) == 2 and patch == 0: 99 | new_version = f"v{major}.{minor}" 100 | else: 101 | new_version = f"v{major}.{minor}.{patch}" 102 | 103 | p = subprocess.run(["just", "_tlog", describe, new_version], 104 | stdout=subprocess.PIPE) 105 | shortlog = p.stdout.decode() 106 | 107 | p = subprocess.run(["just", "_incr_version", new_version]) 108 | if p and p.returncode != 0: 109 | print("Error: _incr_version returned nonzero exit code") 110 | sys.exit(1) 111 | 112 | with tempfile.NamedTemporaryFile() as f: 113 | basename = os.path.basename(os.getcwd()) 114 | f.write(f"{basename} {new_version}\n\n".encode()) 115 | f.write(shortlog.encode()) 116 | f.flush() 117 | subprocess.run(["git", "tag", "-e", "-F", f.name, "-a", new_version]) 118 | print(new_version) 119 | 120 | # Publish a new version on crates.io 121 | publish: 122 | cargo publish 123 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Blue Oak Model License 2 | 3 | Version 1.0.0 4 | 5 | ## Purpose 6 | 7 | This license gives everyone as much permission to work with 8 | this software as possible, while protecting contributors 9 | from liability. 10 | 11 | ## Acceptance 12 | 13 | In order to receive this license, you must agree to its 14 | rules. The rules of this license are both obligations 15 | under that agreement and conditions to your license. 16 | You must not do anything with this software that triggers 17 | a rule that you cannot or will not follow. 18 | 19 | ## Copyright 20 | 21 | Each contributor licenses you to do everything with this 22 | software that would otherwise infringe that contributor's 23 | copyright in it. 24 | 25 | ## Notices 26 | 27 | You must ensure that everyone who gets a copy of 28 | any part of this software from you, with or without 29 | changes, also gets the text of this license or a link to 30 | . 31 | 32 | ## Excuse 33 | 34 | If anyone notifies you in writing that you have not 35 | complied with [Notices](#notices), you can keep your 36 | license by taking all practical steps to comply within 30 37 | days after the notice. If you do not do so, your license 38 | ends immediately. 39 | 40 | ## Patent 41 | 42 | Each contributor licenses you to do everything with this 43 | software that would otherwise infringe any patent claims 44 | they can license or become able to license. 45 | 46 | ## Reliability 47 | 48 | No contributor can revoke this license. 49 | 50 | ## No Liability 51 | 52 | ***As far as the law allows, this software comes as is, 53 | without any warranty or condition, and no contributor 54 | will be liable to anyone for any damages related to this 55 | software or this license, under any kind of legal claim.*** -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ⛓
3 | openring-rs 4 |

5 |
6 | A tool for generating a webring from Atom/RSS feeds. 7 |
8 |
9 |
10 | 11 | Build Status 12 | 13 | 14 | Version 15 | 16 | 17 | License 18 | 19 |
20 |
21 | 22 | `openring-rs` is a tool for generating a webring from Atom/RSS feeds, so you can populate a template with articles from those feeds and embed them in your own blog. An example template is provided in `in.html`. 23 | 24 | This is a rust-port of Drew DeVault's [openring](https://git.sr.ht/~sircmpwn/openring), with the primary differences being: 25 | - we respect throttling and send conditional requests when using `--cache` (recommended!) 26 | - the template is written using [Tera](https://keats.github.io/tera/) and is provided as an argument, not read from stdin 27 | - we show a little progress bar 28 | - we fetch all feeds concurrently 29 | - we provide better error messages (via [miette](https://github.com/zkat/miette)) 30 | - we allow filtering feeds with `--before` 31 | 32 | ## Demo 33 | 34 | To see this in action, you can look at the footer of this blog post. 35 | 36 | 37 | 38 | ## Install 39 | 40 | ``` 41 | cargo install --locked openring 42 | ``` 43 | 44 | ## Usage 45 | 46 | ``` 47 | A webring for static site generators written in Rust 48 | 49 | Usage: openring [OPTIONS] --template-file 50 | 51 | Options: 52 | -n, --num-articles Total number of articles to fetch [default: 3] 53 | -p, --per-source Number of most recent articles to get from each feed [default: 1] 54 | -S, --url-file File with URLs of Atom/RSS feeds to read (one URL per line, lines starting with '#' or "//" are ignored) 55 | -t, --template-file Tera template file 56 | -s, --url A single URL to consider (can be repeated to specify multiple) 57 | -b, --before Only include articles before this date (in YYYY-MM-DD format) 58 | -c, --cache Use request cache stored on disk at `.openringcache` 59 | --max-cache-age Discard all cached requests older than this duration [default: 14d] 60 | -v, --verbose... Increase logging verbosity 61 | -q, --quiet... Decrease logging verbosity 62 | -h, --help Print help (see more with '--help') 63 | -V, --version Print version 64 | ``` 65 | 66 | ## Using Tera Templates 67 | 68 | The templates supported by `openring-rs` are written using [Tera](https://keats.github.io/tera/). 69 | Please refer to the Tera documentation for details. 70 | 71 | ## Why a Rust Port? 72 | 73 | Just for fun. 74 | -------------------------------------------------------------------------------- /bacon.toml: -------------------------------------------------------------------------------- 1 | # This is a configuration file for the bacon tool 2 | # 3 | # Complete help on configuration: https://dystroy.org/bacon/config/ 4 | # 5 | # You may check the current default at 6 | # https://github.com/Canop/bacon/blob/main/defaults/default-bacon.toml 7 | 8 | default_job = "check" 9 | env.CARGO_TERM_COLOR = "always" 10 | 11 | [jobs.check] 12 | command = ["cargo", "check"] 13 | need_stdout = false 14 | 15 | [jobs.check-all] 16 | command = ["cargo", "check", "--all-targets"] 17 | need_stdout = false 18 | 19 | # Run clippy on the default target 20 | [jobs.clippy] 21 | command = ["cargo", "clippy"] 22 | need_stdout = false 23 | 24 | # Run clippy on all targets 25 | # To disable some lints, you may change the job this way: 26 | [jobs.clippy-all] 27 | command = [ 28 | "cargo", "clippy", 29 | "--all-targets", 30 | "--all-features", 31 | "--", 32 | "-W", "clippy::pedantic", 33 | "-D", "warnings", 34 | ] 35 | need_stdout = false 36 | 37 | # This job lets you run 38 | # - all tests: bacon test 39 | # - a specific test: bacon test -- config::test_default_files 40 | # - the tests of a package: bacon test -- -- -p config 41 | [jobs.test] 42 | command = ["cargo", "test"] 43 | need_stdout = true 44 | 45 | [jobs.nextest] 46 | command = [ 47 | "cargo", "nextest", "run", 48 | "--hide-progress-bar", "--failure-output", "final" 49 | ] 50 | need_stdout = true 51 | analyzer = "nextest" 52 | 53 | [jobs.doc] 54 | command = ["cargo", "doc", "--no-deps"] 55 | need_stdout = false 56 | 57 | # If the doc compiles, then it opens in your browser and bacon switches 58 | # to the previous job 59 | [jobs.doc-open] 60 | command = ["cargo", "doc", "--no-deps", "--open"] 61 | need_stdout = false 62 | on_success = "back" # so that we don't open the browser at each change 63 | 64 | # You can run your application and have the result displayed in bacon, 65 | # if it makes sense for this crate. 66 | [jobs.run] 67 | command = [ 68 | "cargo", "run", 69 | # put launch parameters for your program behind a `--` separator 70 | ] 71 | need_stdout = true 72 | allow_warnings = true 73 | background = true 74 | 75 | # Run your long-running application (eg server) and have the result displayed in bacon. 76 | # For programs that never stop (eg a server), `background` is set to false 77 | # to have the cargo run output immediately displayed instead of waiting for 78 | # program's end. 79 | # 'on_change_strategy' is set to `kill_then_restart` to have your program restart 80 | # on every change (an alternative would be to use the 'F5' key manually in bacon). 81 | # If you often use this job, it makes sense to override the 'r' key by adding 82 | # a binding `r = job:run-long` at the end of this file . 83 | [jobs.run-long] 84 | command = [ 85 | "cargo", "run", 86 | # put launch parameters for your program behind a `--` separator 87 | ] 88 | need_stdout = true 89 | allow_warnings = true 90 | background = false 91 | on_change_strategy = "kill_then_restart" 92 | 93 | # This parameterized job runs the example of your choice, as soon 94 | # as the code compiles. 95 | # Call it as 96 | # bacon ex -- my-example 97 | [jobs.ex] 98 | command = ["cargo", "run", "--example"] 99 | need_stdout = true 100 | allow_warnings = true 101 | 102 | # You may define here keybindings that would be specific to 103 | # a project, for example a shortcut to launch a specific job. 104 | # Shortcuts to internal functions (scrolling, toggling, etc.) 105 | # should go in your personal global prefs.toml file instead. 106 | [keybindings] 107 | # alt-m = "job:my-job" 108 | c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target 109 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ configuration file 2 | # https://git-cliff.org/docs/configuration 3 | 4 | [changelog] 5 | # changelog header 6 | header = """ 7 | # Changelog\n 8 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.\n 9 | """ 10 | # template for the changelog body 11 | # https://keats.github.io/tera/docs/#introduction 12 | body = """ 13 | --- 14 | {% if version %}\ 15 | {% if previous.version %}\ 16 | ## [{{ version | trim_start_matches(pat="v") }}]($REPO/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} 17 | {% else %}\ 18 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 19 | {% endif %}\ 20 | {% else %}\ 21 | ## [unreleased] 22 | {% endif %}\ 23 | {% for group, commits in commits | group_by(attribute="group") %} 24 | ### {{ group | striptags | trim | upper_first }} 25 | {% for commit in commits 26 | | filter(attribute="scope") 27 | | sort(attribute="scope") %} 28 | - **({{commit.scope}})**{% if commit.breaking %} [**breaking**]{% endif %} \ 29 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 30 | {%- endfor -%} 31 | {% raw %}\n{% endraw %}\ 32 | {%- for commit in commits %} 33 | {%- if commit.scope -%} 34 | {% else -%} 35 | - {% if commit.breaking %} [**breaking**] {% endif %}\ 36 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 37 | {% endif -%} 38 | {% endfor -%} 39 | {% endfor %}\n 40 | """ 41 | # template for the changelog footer 42 | footer = """ 43 | """ 44 | # remove the leading and trailing whitespace from the templates 45 | trim = true 46 | # postprocessors 47 | postprocessors = [ 48 | { pattern = '\$REPO', replace = "https://github.com/lukehsiao/openring-rs" }, # replace repository URL 49 | ] 50 | 51 | [git] 52 | # parse the commits based on https://www.conventionalcommits.org 53 | conventional_commits = true 54 | # filter out the commits that are not conventional 55 | filter_unconventional = true 56 | # regex for parsing and grouping commits 57 | commit_parsers = [ 58 | { body = ".*security", group = "Security"}, 59 | { message = "^feat", group = "Features"}, 60 | { message = "^fix", group = "Bug Fixes"}, 61 | { message = "^perf", group = "Performance"}, 62 | { message = "^doc", group = "Documentation"}, 63 | { message = "^refactor", group = "Refactor"}, 64 | { message = "^build", group = "Build and Dependencies" }, 65 | { message = "^test", group = "Testing", skip = true}, 66 | { message = "^style", group = "Styling", skip = true}, 67 | { message = "^ci", group = "CI/CD", skip = true}, 68 | { message = "^chore\\(release\\): bump", skip = true}, 69 | { message = "^chore", group = "Miscellaneous Tasks", skip = true}, 70 | ] 71 | # filter out the commits that are not matched by commit parsers 72 | filter_commits = false 73 | # glob pattern for matching git tags 74 | tag_pattern = "v[0-9]*" 75 | # regex for skipping tags 76 | skip_tags = "v0.1.0-beta.1" 77 | # regex for ignoring tags 78 | ignore_tags = "python-v[0-9]*" 79 | # sort the tags chronologically 80 | date_order = true 81 | # sort the commits inside sections by oldest/newest order 82 | sort_commits = "oldest" 83 | link_parsers = [ 84 | { pattern = "#(\\d+)", href = "https://github.com/lukehsiao/openring-rs/issues/$1"}, 85 | { pattern = "RFC(\\d+)", text = "ietf-rfc$1", href = "https://datatracker.ietf.org/doc/html/rfc$1"}, 86 | ] 87 | -------------------------------------------------------------------------------- /in.html: -------------------------------------------------------------------------------- 1 |
2 |

Posts from blogs I follow

3 |
4 | {% for article in articles %} 5 |
6 |

7 | {{ article.title | safe }} 8 |

9 |

{{ article.summary | linebreaksbr | replace(from="
", to=" ") | striptags | trim 10 | | truncate(length=256) | safe }}

11 | via {{ article.source_title | safe 12 | }} 13 | {{ article.timestamp | date(format="%B %d, %Y") }} 14 |
15 | {% endfor %} 16 |
17 |

18 | Generated by 19 | openring-rs 20 |

21 |
22 | 59 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, time::Duration}; 2 | 3 | use clap::{Parser, builder::ValueHint}; 4 | use clap_verbosity_flag::Verbosity; 5 | use jiff::civil::Date; 6 | use url::Url; 7 | 8 | #[derive(Parser, Debug)] 9 | #[command(author, version, about, long_about = None)] 10 | pub struct Args { 11 | /// Total number of articles to fetch 12 | #[arg(short, long, default_value_t = 3)] 13 | pub num_articles: usize, 14 | /// Number of most recent articles to get from each feed 15 | #[arg(short, long, default_value_t = 1)] 16 | pub per_source: usize, 17 | /// File with URLs of Atom/RSS feeds to read (one URL per line, lines starting with '#' or "//" are ignored) 18 | #[arg(short = 'S', long, value_name = "FILE", value_hint=ValueHint::FilePath)] 19 | pub url_file: Option, 20 | /// Tera template file 21 | #[arg(short, long, value_parser, value_name = "FILE", value_hint=ValueHint::FilePath)] 22 | pub template_file: PathBuf, 23 | /// A single URL to consider (can be repeated to specify multiple) 24 | #[arg(short = 's', long, value_hint=ValueHint::Url)] 25 | pub url: Vec, 26 | /// Only include articles before this date (in YYYY-MM-DD format). 27 | /// 28 | /// This is naive (no timezone), so articles close to the boundary in different timezones might 29 | /// be unexpectedly filtered. In addition, some feeds are truncated, and may have already pruned 30 | /// away articles before this date from the feed itself. 31 | #[arg(short, long)] 32 | pub before: Option, 33 | /// Use request cache stored on disk at `.openringcache` 34 | /// 35 | /// Note that this only prevents refetching if the feed source responds 36 | /// with a 429. In this case, we respect Retry-After, or default to 4h. 37 | /// Otherwise, the existence of a cache file just allows openring to respect 38 | /// `ETag` and `Last-Modified` headers for conditional requests. 39 | #[arg(short, long)] 40 | pub cache: bool, 41 | /// Discard all cached requests older than this duration 42 | #[arg( 43 | long, 44 | value_parser = humantime::parse_duration, 45 | default_value = "14d" 46 | )] 47 | pub max_cache_age: Duration, 48 | #[clap(flatten)] 49 | pub verbose: Verbosity, 50 | } 51 | 52 | #[cfg(test)] 53 | mod test { 54 | use crate::*; 55 | #[test] 56 | fn verify_app() { 57 | use clap::CommandFactory; 58 | Args::command().debug_assert(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp::Ordering, fs, path::Path, time::Duration}; 2 | 3 | use dashmap::DashMap; 4 | use jiff::{Span, Timestamp, ToSpan}; 5 | use serde::{Deserialize, Serialize}; 6 | use tracing::{info, warn}; 7 | use url::Url; 8 | 9 | use crate::{args::Args, error::Result}; 10 | 11 | pub(crate) const OPENRING_CACHE_FILE: &str = ".openringcache"; 12 | 13 | /// Describes a feed fetch result that can be serialized to disk 14 | #[derive(Serialize, Deserialize, Debug)] 15 | pub(crate) struct CacheValue { 16 | pub(crate) timestamp: Timestamp, 17 | pub(crate) retry_after: Option, 18 | pub(crate) last_modified: Option, 19 | pub(crate) etag: Option, 20 | pub(crate) body: Option, 21 | } 22 | 23 | pub(crate) type Cache = DashMap; 24 | 25 | pub(crate) trait StoreExt { 26 | /// Store the cache under the given path. Update access timestamps 27 | fn store>(&self, path: T) -> Result<()>; 28 | 29 | /// Load cache from path. Discard entries older than `max_age_secs` 30 | fn load>(path: T, max_age_secs: u64) -> Result; 31 | } 32 | 33 | impl StoreExt for Cache { 34 | fn store>(&self, path: T) -> Result<()> { 35 | let mut wtr = csv::WriterBuilder::new() 36 | .has_headers(false) 37 | .from_path(path)?; 38 | for result in self { 39 | wtr.serialize((result.key(), result.value()))?; 40 | } 41 | Ok(()) 42 | } 43 | 44 | fn load>(path: T, max_age_secs: u64) -> Result { 45 | let mut rdr = csv::ReaderBuilder::new() 46 | .has_headers(false) 47 | .from_path(path)?; 48 | 49 | let map = DashMap::new(); 50 | let current_ts = Timestamp::now(); 51 | for result in rdr.deserialize() { 52 | let (url, value): (Url, CacheValue) = result?; 53 | // Discard entries older than `max_age_secs`. 54 | // This allows gradually updating the cache over multiple runs. 55 | if (current_ts - value.timestamp).compare(i64::try_from(max_age_secs)?.seconds())? 56 | == Ordering::Less 57 | { 58 | map.insert(url, value); 59 | } 60 | } 61 | Ok(map) 62 | } 63 | } 64 | 65 | /// Load cache (if exists and is still valid). 66 | /// This returns an `Option` as starting without a cache is a common scenario 67 | /// and we silently discard errors on purpose. 68 | pub(crate) fn load_cache(args: &Args) -> Option { 69 | if !args.cache { 70 | return None; 71 | } 72 | 73 | // Discard entire cache if it hasn't been updated since `max_cache_age`. 74 | // This is an optimization, which avoids iterating over the file and 75 | // checking the age of each entry. 76 | match fs::metadata(OPENRING_CACHE_FILE) { 77 | Err(_e) => { 78 | // No cache found; silently start with empty cache 79 | return None; 80 | } 81 | Ok(metadata) => { 82 | let modified = metadata.modified().ok()?; 83 | let elapsed = modified.elapsed().ok()?; 84 | if elapsed > args.max_cache_age { 85 | warn!( 86 | "Cache is too old (age: {:#?}, max age: {:#?}). Discarding and recreating.", 87 | Duration::from_secs(elapsed.as_secs()), 88 | Duration::from_secs(args.max_cache_age.as_secs()) 89 | ); 90 | return None; 91 | } 92 | info!( 93 | "Cache is recent (age: {:#?}, max age: {:#?}). Using.", 94 | Duration::from_secs(elapsed.as_secs()), 95 | Duration::from_secs(args.max_cache_age.as_secs()) 96 | ); 97 | } 98 | } 99 | 100 | let cache = Cache::load(OPENRING_CACHE_FILE, args.max_cache_age.as_secs()); 101 | match cache { 102 | Ok(cache) => Some(cache), 103 | Err(e) => { 104 | warn!("Error while loading cache: {e}. Continuing without."); 105 | None 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_name_repetitions)] 2 | use std::result; 3 | 4 | use miette::{Diagnostic, NamedSource, SourceSpan}; 5 | use thiserror::Error; 6 | 7 | pub(crate) type Result = result::Result; 8 | 9 | #[derive(Error, Debug, Diagnostic)] 10 | pub enum OpenringError { 11 | #[error("No valid published or updated date found.")] 12 | DateError, 13 | #[error("No feed urls were provided. Provide feeds with -s or -S .")] 14 | FeedMissing, 15 | #[error("The feed at `{0}` has a bad a title (e.g., missing link or title).")] 16 | #[diagnostic(code(openring::feed_title_error))] 17 | FeedBadTitle(String), 18 | #[error("Failed to parse civil date.")] 19 | CivilDateError(#[from] jiff::Error), 20 | #[error(transparent)] 21 | ReqwestError(#[from] reqwest::Error), 22 | #[error(transparent)] 23 | #[diagnostic(transparent)] 24 | ChronoError(#[from] ChronoError), 25 | #[error(transparent)] 26 | #[diagnostic(transparent)] 27 | FeedUrlError(#[from] FeedUrlError), 28 | #[error("The feed at `{0}` was empty.")] 29 | #[diagnostic(code(openring::empty_feed_error))] 30 | EmptyFeedError(String), 31 | #[error("The request feed at `{0}` was rate limited (HTTP 429).")] 32 | #[diagnostic(code(openring::rate_limit_error))] 33 | RateLimitError(String), 34 | #[error("The request feed at `{url}` received an unexpected error (HTTP {status}).")] 35 | #[diagnostic(code(openring::unexpected_status_error))] 36 | UnexpectedStatusError { url: String, status: String }, 37 | #[error(transparent)] 38 | #[diagnostic(code(openring::parse_feed_error))] 39 | ParseFeedError(#[from] feed_rs::parser::ParseFeedError), 40 | #[error("Failed to open file.")] 41 | #[diagnostic(code(openring::io_error))] 42 | IoError(#[from] std::io::Error), 43 | #[error("Failed to parse URL.")] 44 | #[diagnostic(code(openring::url_parse_error))] 45 | UrlParseError(#[from] url::ParseError), 46 | #[error("Failed to parse tera template.")] 47 | #[diagnostic(code(openring::template_error))] 48 | TemplateError(#[from] tera::Error), 49 | #[error("Invalid cache file found.")] 50 | #[diagnostic(code(openring::cache_error))] 51 | CsvError(#[from] csv::Error), 52 | #[error("Invalid cache file found.")] 53 | #[diagnostic(code(openring::cache_error))] 54 | TryFromIntError(#[from] std::num::TryFromIntError), 55 | } 56 | 57 | #[derive(Error, Diagnostic, Debug)] 58 | #[error("Failed to parse datetime.")] 59 | #[diagnostic(code(openring::chrono_error))] 60 | pub struct ChronoError { 61 | #[source_code] 62 | pub src: NamedSource, 63 | #[label("this date is invalid")] 64 | pub span: SourceSpan, 65 | #[help] 66 | pub help: String, 67 | } 68 | 69 | #[derive(Error, Diagnostic, Debug)] 70 | #[error("Failed to parse feed url.")] 71 | #[diagnostic(code(openring::url_parse_error))] 72 | pub struct FeedUrlError { 73 | #[source_code] 74 | pub src: NamedSource, 75 | #[label("this url is invalid")] 76 | pub span: SourceSpan, 77 | #[help] 78 | pub help: String, 79 | } 80 | -------------------------------------------------------------------------------- /src/feedfetcher.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::too_many_lines)] 2 | 3 | use std::{sync::Arc, time::Duration}; 4 | 5 | use clap::{crate_name, crate_version}; 6 | use feed_rs::{model::Feed, parser}; 7 | use jiff::{Timestamp, ToSpan}; 8 | use reqwest::{ 9 | StatusCode, {Client, ClientBuilder}, 10 | }; 11 | use tracing::{debug, warn}; 12 | use url::Url; 13 | 14 | use crate::{ 15 | cache::{Cache, CacheValue}, 16 | error::OpenringError, 17 | }; 18 | 19 | pub(crate) trait FeedFetcher { 20 | /// Fetch a feed 21 | async fn fetch_feed(&self, cache: &Arc) -> Result; 22 | } 23 | 24 | impl FeedFetcher for Url { 25 | /// Fetch a feed for a URL 26 | async fn fetch_feed(&self, cache: &Arc) -> Result { 27 | let client: Client = ClientBuilder::new() 28 | .timeout(Duration::from_secs(30)) 29 | .user_agent(concat!(crate_name!(), '/', crate_version!())) 30 | .build()?; 31 | 32 | let req = { 33 | let cache_value = cache.get(self); 34 | 35 | // Respect Retry-After Header if set in cache 36 | if let Some(ref cv) = cache_value { 37 | if let Some(retry) = cv.retry_after { 38 | if cv.timestamp + retry > Timestamp::now() { 39 | debug!(timestamp=%cv.timestamp, retry_after=%retry, "skipping request due to 429, using feed from cache"); 40 | 41 | // TODO: This is just copy-pasted, should be reused 42 | if let Some(ref feed_str) = cv.body { 43 | return match parser::parse(feed_str.as_bytes()) { 44 | Ok(feed) => Ok(feed), 45 | Err(e) => { 46 | warn!( 47 | url=%self.as_str(), 48 | error=%e, 49 | "failed to parse feed." 50 | ); 51 | Err(e.into()) 52 | } 53 | }; 54 | } 55 | warn!(url = %self.as_str(), "empty feed"); 56 | } 57 | } 58 | } 59 | 60 | let mut r = client.get(self.as_str()); 61 | // Add friendly headers if cache is available 62 | if let Some(ref cv) = cache_value { 63 | if let Some(last_modified) = &cv.last_modified { 64 | r = r.header("If-Modified-Since", last_modified); 65 | } 66 | if let Some(etag) = &cv.etag { 67 | r = r.header("If-None-Match", etag); 68 | } 69 | } 70 | debug!(url=%self, request=?r, "sending request"); 71 | r 72 | }; 73 | 74 | let body = match req.send().await { 75 | Ok(r) => { 76 | debug!(url=%self, response=?r, "received response"); 77 | match r.status() { 78 | s if s.is_success() || s == StatusCode::NOT_MODIFIED => { 79 | // ETag values must have the actual quotes 80 | let etag = r.headers().get("etag").and_then(|etag_value| { 81 | // Convert header to str 82 | etag_value.to_str().ok().map(|etag_str| { 83 | if (etag_str.starts_with('"') && etag_str.ends_with('"')) 84 | || (etag_str.starts_with("W/\"") && etag_str.ends_with('"')) 85 | { 86 | etag_str.to_string() 87 | } else { 88 | format!("\"{etag_str}\"") 89 | } 90 | }) 91 | }); 92 | let last_modified = r.headers().get("last-modified").and_then(|lm_value| { 93 | lm_value.to_str().ok().map(std::string::ToString::to_string) 94 | }); 95 | let status = r.status(); 96 | let mut body = r.text().await.ok(); 97 | 98 | // Update cache 99 | { 100 | let cache_value = cache.get_mut(self); 101 | if let Some(mut cv) = cache_value { 102 | if status == StatusCode::NOT_MODIFIED { 103 | debug!(url=%self, status=status.as_str(), "got 304, using feed from cache"); 104 | body.clone_from(&cv.body); 105 | } else { 106 | debug!(url=%self, status=status.as_str(), "cache hit, using feed from body"); 107 | cv.etag = etag; 108 | cv.last_modified = last_modified; 109 | cv.body.clone_from(&body); 110 | } 111 | cv.timestamp = Timestamp::now(); 112 | } else { 113 | debug!(url=%self, status=status.as_str(), "using feed from body and adding to cache"); 114 | cache.insert( 115 | self.clone(), 116 | CacheValue { 117 | timestamp: Timestamp::now(), 118 | retry_after: None, 119 | etag, 120 | last_modified, 121 | body: body.clone(), 122 | }, 123 | ); 124 | } 125 | } 126 | body.ok_or(OpenringError::EmptyFeedError(self.as_str().to_string())) 127 | } 128 | StatusCode::TOO_MANY_REQUESTS => { 129 | let cache_value = cache.get_mut(self); 130 | if let Some(mut cv) = cache_value { 131 | cv.timestamp = Timestamp::now(); 132 | // Default to waiting 4 hrs if no Retry-After 133 | let retry_after = r 134 | .headers() 135 | .get("retry-after") 136 | .and_then(|retry_value| { 137 | retry_value.to_str().ok().map(|retry_str| { 138 | retry_str.parse::().map(ToSpan::seconds).ok() 139 | }) 140 | }) 141 | .unwrap_or(Some(4.hours())); 142 | debug!(url=%self, response=?r, "got 429, using feed from cache"); 143 | cv.timestamp = Timestamp::now(); 144 | cv.retry_after = retry_after; 145 | cv.body 146 | .clone() 147 | .ok_or(OpenringError::EmptyFeedError(self.as_str().to_string())) 148 | } else { 149 | Err(OpenringError::RateLimitError(self.as_str().to_string())) 150 | } 151 | } 152 | unexpected => Err(OpenringError::UnexpectedStatusError { 153 | url: self.as_str().to_string(), 154 | status: unexpected.as_str().to_string(), 155 | }), 156 | } 157 | } 158 | Err(e) => { 159 | warn!(url=%self.as_str(), error=%e, "failed to get feed."); 160 | Err(e.into()) 161 | } 162 | }; 163 | 164 | match body { 165 | Ok(feed_str) => match parser::parse(feed_str.as_bytes()) { 166 | Ok(feed) => Ok(feed), 167 | Err(e) => { 168 | warn!( 169 | url=%self.as_str(), 170 | error=%e, 171 | "failed to parse feed." 172 | ); 173 | Err(e.into()) 174 | } 175 | }, 176 | Err(e) => Err(e), 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod args; 2 | pub mod cache; 3 | pub mod error; 4 | pub mod feedfetcher; 5 | 6 | use std::{ 7 | collections::HashSet, 8 | fs::{self, File}, 9 | io::{BufRead, BufReader}, 10 | path::Path, 11 | sync::Arc, 12 | }; 13 | 14 | use feed_rs::model::Feed; 15 | use indicatif::{ProgressBar, ProgressStyle}; 16 | use jiff::{Timestamp, tz::TimeZone}; 17 | use miette::NamedSource; 18 | use serde::Serialize; 19 | use tera::Tera; 20 | use tokio::task::JoinSet; 21 | use tracing::{debug, info, warn}; 22 | use url::{ParseError, Url}; 23 | use yansi::Paint; 24 | 25 | use crate::{ 26 | args::Args, 27 | cache::{Cache, OPENRING_CACHE_FILE, StoreExt}, 28 | error::{FeedUrlError, OpenringError, Result}, 29 | feedfetcher::FeedFetcher, 30 | }; 31 | 32 | #[derive(Serialize, Debug)] 33 | pub struct Article { 34 | link: Url, 35 | title: String, 36 | summary: String, 37 | source_link: Url, 38 | source_title: String, 39 | timestamp: Timestamp, 40 | } 41 | 42 | /// Parse the file into a vector of URLs. 43 | fn parse_urls_from_file(path: &Path) -> Result> { 44 | let file = File::open(path)?; 45 | let reader = BufReader::new(file); 46 | 47 | reader 48 | .lines() 49 | // Allow '#' or "//" comments in the urls file 50 | .filter(|l| { 51 | let line = l.as_ref().unwrap(); 52 | let trimmed = line.trim(); 53 | !(trimmed.starts_with('#') || trimmed.starts_with("//")) 54 | }) 55 | .map(|line| { 56 | let line = &line.unwrap(); 57 | Url::parse(line).map_err(|e| { 58 | // Give a nice diagnostic error 59 | let file_src = fs::read_to_string(path).unwrap(); 60 | let offset = file_src.find(line).unwrap(); 61 | FeedUrlError { 62 | src: NamedSource::new( 63 | path.to_path_buf().into_os_string().to_string_lossy(), 64 | file_src, 65 | ), 66 | span: (offset..offset + line.len()).into(), 67 | help: e.to_string(), 68 | } 69 | .into() 70 | }) 71 | }) 72 | .collect() 73 | } 74 | 75 | // Get all feeds from URLs concurrently. 76 | // 77 | // Skips feeds if there are errors. Shows progress. 78 | async fn get_feeds_from_urls(urls: &[Url], cache: &Arc) -> Vec<(Feed, Url)> { 79 | let pb = ProgressBar::new(urls.len() as u64).with_style( 80 | ProgressStyle::with_template("{prefix:>8} [{bar}] {human_pos}/{human_len}: {wide_msg}") 81 | .unwrap(), 82 | ); 83 | pb.set_prefix("Fetching".bold().to_string()); 84 | 85 | let mut join_set = JoinSet::new(); 86 | let mut pending_urls: HashSet<&Url> = HashSet::from_iter(urls); 87 | 88 | pb.set_message( 89 | pending_urls 90 | .iter() 91 | .map(|u| u.as_str()) 92 | .collect::>() 93 | .join(", "), 94 | ); 95 | 96 | for url in urls { 97 | let cache_clone = Arc::clone(cache); 98 | let url_clone = url.clone(); 99 | join_set.spawn(async move { 100 | let fetch_result = url_clone.fetch_feed(&cache_clone).await; 101 | (url_clone, fetch_result) 102 | }); 103 | } 104 | let mut feeds = Vec::new(); 105 | 106 | while let Some(result) = join_set.join_next().await { 107 | pb.inc(1); 108 | match result { 109 | Ok((url, Ok(feed))) => { 110 | pending_urls.remove(&url); 111 | pb.set_message( 112 | pending_urls 113 | .iter() 114 | .map(|u| u.as_str()) 115 | .collect::>() 116 | .join(", "), 117 | ); 118 | pb.println(format!("{:>8} {url}", "Fetched".bold().green())); 119 | feeds.push((feed, url)); 120 | } 121 | Ok((url, Err(e))) => { 122 | pending_urls.remove(&url); 123 | pb.set_message( 124 | pending_urls 125 | .iter() 126 | .map(|u| u.as_str()) 127 | .collect::>() 128 | .join(", "), 129 | ); 130 | pb.println(format!("{:>8} {url} ({e})", "Error".bold().red())); 131 | } 132 | _ => (), 133 | } 134 | } 135 | 136 | pb.finish_and_clear(); 137 | feeds 138 | } 139 | 140 | #[allow(clippy::missing_panics_doc)] 141 | #[allow(clippy::missing_errors_doc)] 142 | #[allow(clippy::too_many_lines)] 143 | pub async fn run(args: Args) -> Result<()> { 144 | debug!(?args); 145 | let cache = cache::load_cache(&args).unwrap_or_default(); 146 | let cache = Arc::new(cache); 147 | 148 | let mut urls = args.url; 149 | 150 | if let Some(path) = args.url_file { 151 | let mut file_urls = parse_urls_from_file(&path)?; 152 | urls.append(&mut file_urls); 153 | }; 154 | 155 | if urls.is_empty() { 156 | return Err(OpenringError::FeedMissing); 157 | } 158 | 159 | // Deduplicate 160 | let urls: Vec = { 161 | let unique: HashSet = urls.into_iter().collect(); 162 | unique.into_iter().collect() 163 | }; 164 | 165 | let feeds = get_feeds_from_urls(&urls, &cache).await; 166 | 167 | if args.cache { 168 | cache.store(OPENRING_CACHE_FILE)?; 169 | } 170 | 171 | let template = fs::read_to_string(&args.template_file)?; 172 | let mut context = tera::Context::new(); 173 | 174 | // Grab articles from all the feeds 175 | let mut articles = Vec::new(); 176 | for (feed, url) in feeds { 177 | let entries = if feed.entries.len() >= args.per_source { 178 | &feed.entries[0..args.per_source] 179 | } else { 180 | &feed.entries 181 | }; 182 | 183 | let source_title = match feed.title { 184 | Some(ref t) => { 185 | if t.content.is_empty() { 186 | url.domain().unwrap().to_owned() 187 | } else { 188 | t.content.clone() 189 | } 190 | } 191 | None => url.domain().unwrap().to_owned(), 192 | }; 193 | let source_link = match &feed.title.as_ref().unwrap().src { 194 | None => { 195 | // Then, look for links 196 | match feed 197 | .links 198 | .iter() 199 | .find(|l| { 200 | if let Some(rel) = &l.rel { 201 | rel == "alternate" 202 | } else { 203 | false 204 | } 205 | }) 206 | .map(|l| &l.href) 207 | { 208 | None => { 209 | // If an alternate link is missing just grab one of them 210 | match feed 211 | .links 212 | .into_iter() 213 | // Ignore "self" rels, which usually link to feed 214 | .find(|l| l.rel.as_ref().is_none_or(|r| r != "self")) 215 | .map(|l| l.href) 216 | { 217 | Some(s) => { 218 | match Url::parse(&s) { 219 | Ok(u) => u, 220 | Err(ParseError::RelativeUrlWithoutBase) => Url::parse( 221 | &format!("{}{}", url.origin().ascii_serialization(), &s), 222 | )?, 223 | Err(e) => return Err(OpenringError::UrlParseError(e)), 224 | } 225 | } 226 | None => return Err(OpenringError::FeedBadTitle(url.to_string())), 227 | } 228 | } 229 | Some(s) => match Url::parse(s) { 230 | Ok(u) => u, 231 | Err(ParseError::RelativeUrlWithoutBase) => { 232 | Url::parse(&format!("{}{}", url.origin().ascii_serialization(), &s))? 233 | } 234 | Err(e) => return Err(OpenringError::UrlParseError(e)), 235 | }, 236 | } 237 | } 238 | Some(s) => match Url::parse(s) { 239 | Ok(u) => u, 240 | Err(ParseError::RelativeUrlWithoutBase) => { 241 | Url::parse(&format!("{}{}", url.origin().ascii_serialization(), &s))? 242 | } 243 | Err(e) => return Err(OpenringError::UrlParseError(e)), 244 | }, 245 | }; 246 | for entry in entries { 247 | if let (Some(link), Some(title), Some(date)) = 248 | ( 249 | match entry 250 | .links 251 | .iter() 252 | .find(|l| { 253 | if let Some(rel) = &l.rel { 254 | rel == "alternate" 255 | } else { 256 | false 257 | } 258 | }) 259 | .map(|l| &l.href) 260 | { 261 | Some(s) => match Url::parse(s) { 262 | Ok(u) => Some(u), 263 | Err(ParseError::RelativeUrlWithoutBase) => { 264 | Url::parse(&format!("{}{}", url.origin().ascii_serialization(), &s)) 265 | .ok() 266 | } 267 | Err(_) => None, 268 | }, 269 | None => { 270 | // If an alternate link is missing just grab one of them 271 | match entry.links.clone().into_iter().next().map(|l| l.href) { 272 | Some(s) => match Url::parse(&s) { 273 | Ok(u) => Some(u), 274 | Err(ParseError::RelativeUrlWithoutBase) => Url::parse( 275 | &format!("{}{}", url.origin().ascii_serialization(), &s), 276 | ) 277 | .ok(), 278 | Err(_) => None, 279 | }, 280 | None => return Err(OpenringError::FeedBadTitle(url.to_string())), 281 | } 282 | } 283 | }, 284 | entry.title.as_ref().map(|t| &t.content), 285 | entry.published.or(entry.updated), 286 | ) 287 | { 288 | // Skip articles after args.before, if present 289 | let timestamp = Timestamp::from_second(date.timestamp())?; 290 | if let Some(before) = args.before { 291 | if timestamp > before.to_zoned(TimeZone::system())?.timestamp() { 292 | continue; 293 | } 294 | } 295 | 296 | let summary = match &entry.summary { 297 | Some(s) => &s.content, 298 | None => { 299 | if let Some(c) = &entry.content { 300 | if let Some(b) = &c.body { 301 | b 302 | } else { 303 | info!(?link, ?source_link, "no summary or content provided."); 304 | "" 305 | } 306 | } else { 307 | info!(?link, ?source_link, "no summary or content provided."); 308 | "" 309 | } 310 | } 311 | }; 312 | 313 | let mut safe_summary = String::new(); 314 | html_escape::decode_html_entities_to_string( 315 | ammonia::clean(summary), 316 | &mut safe_summary, 317 | ); 318 | articles.push(Article { 319 | link, 320 | title: title.to_string(), 321 | summary: safe_summary.trim().to_string(), 322 | source_link: source_link.clone(), 323 | source_title: source_title.clone(), 324 | timestamp, 325 | }); 326 | } else { 327 | warn!( 328 | entry_links=?entry.links, 329 | entry_title=?entry.title, 330 | entry_published=?entry.published, 331 | entry_updated=?entry.updated, 332 | source=url.as_str(), 333 | "skipping entry: must have link, title, and a date." 334 | ); 335 | } 336 | } 337 | } 338 | 339 | articles.sort_unstable_by(|a, b| a.timestamp.cmp(&b.timestamp).reverse()); 340 | let articles = if articles.len() >= args.num_articles { 341 | &articles[0..args.num_articles] 342 | } else { 343 | &articles 344 | }; 345 | 346 | context.insert("articles", articles); 347 | // TODO: this validation of the template should come before all the time spent fetching feeds. 348 | let output = Tera::one_off(&template, &context, true)?; 349 | println!("{output}"); 350 | Ok(()) 351 | } 352 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use miette::{Context, Result}; 3 | use std::io; 4 | use tracing_log::AsTrace; 5 | 6 | use openring::{self, args::Args}; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | let args = Args::parse(); 11 | 12 | tracing_subscriber::fmt() 13 | .with_env_filter(format!( 14 | "openring={},html5ever=off", 15 | args.verbose.log_level_filter().as_trace() 16 | )) 17 | .with_writer(io::stderr) 18 | .init(); 19 | // I feel like I shouldn't need wrap_err, but it doesn't work without it. 20 | openring::run(args).await.wrap_err("runtime error") 21 | } 22 | --------------------------------------------------------------------------------