├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── homebrew.yml │ ├── pages.yml │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dioxus.toml ├── LICENSE ├── README.md ├── crates ├── html-query-ast │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── parser.rs ├── html-query-extractor │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── html-query-web-ui │ ├── Cargo.toml │ └── src │ │ ├── examples │ │ ├── examples.json │ │ └── hn.html │ │ └── main.rs └── html-query │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── tests │ └── whitespace.html ├── images └── readme-example.gif └── index.html /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | [*.{rs,json,yml,md}] 9 | charset = utf-8 10 | 11 | [*.yml] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | groups: 13 | dependencies: 14 | patterns: 15 | - "*" 16 | - package-ecosystem: "github-actions" # See documentation for possible values 17 | directory: "/" # Location of package manifests 18 | schedule: 19 | interval: "daily" 20 | groups: 21 | dependencies: 22 | patterns: 23 | - "*" 24 | -------------------------------------------------------------------------------- /.github/workflows/homebrew.yml: -------------------------------------------------------------------------------- 1 | name: Homebrew Bump 2 | on: 3 | push: 4 | tags: 5 | - 'html-query-v*' 6 | 7 | jobs: 8 | homebrew: 9 | name: Bump Homebrew formula 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Extract version 13 | id: extract-version 14 | run: | 15 | echo "VERSION=${GITHUB_REF#refs/tags/html-query-}" >> "$GITHUB_OUTPUT" 16 | - uses: mislav/bump-homebrew-formula-action@v3 17 | with: 18 | formula-name: hq 19 | commit-message: | 20 | hq ${{ steps.extract-version.outputs.VERSION }} 21 | 22 | Created by https://github.com/mislav/bump-homebrew-formula-action 23 | env: 24 | COMMITTER_TOKEN: ${{ secrets.COMMITTER_TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Publish pages 2 | concurrency: "pages" 3 | 4 | on: 5 | push: 6 | branches: 7 | - '*' 8 | tags: 9 | - 'html-query-*' 10 | workflow_dispatch: 11 | inputs: 12 | tag: 13 | required: true 14 | description: 'The tag name to use for the release' 15 | 16 | jobs: 17 | # Build job 18 | build: 19 | runs-on: ubuntu-latest 20 | env: 21 | SCCACHE_GHA_ENABLED: "true" 22 | RUSTC_WRAPPER: "sccache" 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Install Rust WASM target 27 | uses: actions-rust-lang/setup-rust-toolchain@v1 28 | with: 29 | cache: 'false' 30 | cache-on-failure: false 31 | target: wasm32-unknown-unknown 32 | 33 | - name: Install Rust 34 | id: rust 35 | uses: actions-rust-lang/setup-rust-toolchain@v1 36 | with: 37 | cache: 'false' 38 | cache-on-failure: false 39 | toolchain: stable 40 | 41 | - name: Run sccache-cache 42 | uses: mozilla-actions/sccache-action@v0.0.9 43 | 44 | - name: Install dioxus 45 | shell: bash 46 | run: | 47 | cargo install dioxus-cli 48 | 49 | - name: Build debug 50 | if: ${{!startsWith(github.ref, 'refs/tags/') && github.event_name != 'workflow_dispatch'}} 51 | shell: bash 52 | run: dx build --package=html-query-web-ui 53 | 54 | - name: Build Release 55 | if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' 56 | shell: bash 57 | run: dx build --release --package=html-query-web-ui 58 | 59 | - name: Upload artifact 60 | if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' 61 | uses: actions/upload-pages-artifact@v3 62 | with: 63 | path: ./html-query-web-ui/dist/ 64 | retention-days: 1 65 | 66 | - name: Create Bundle 67 | run: | 68 | tar czf wasm.tar.gz --directory=html-query-web-ui/dist/ . 69 | 70 | - name: Release 71 | uses: softprops/action-gh-release@v2 72 | if: startsWith(github.ref, 'refs/tags/') 73 | with: 74 | draft: false 75 | files: wasm.tar.gz 76 | 77 | - name: Release 78 | uses: softprops/action-gh-release@v2 79 | if: github.event_name == 'workflow_dispatch' 80 | with: 81 | draft: false 82 | tag_name: ${{ github.event.inputs.tag }} 83 | files: wasm.tar.gz 84 | 85 | 86 | deploy: 87 | # Add a dependency to the build job 88 | needs: build 89 | if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' 90 | 91 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 92 | permissions: 93 | pages: write # to deploy to Pages 94 | id-token: write # to verify the deployment originates from an appropriate source 95 | 96 | # # Deploy to the github-pages environment 97 | # environment: 98 | # name: github-pages 99 | # url: ${{ steps.deployment.outputs.page_url }} 100 | 101 | # Specify runner + deployment step 102 | runs-on: ubuntu-latest 103 | steps: 104 | - name: Deploy to GitHub Pages 105 | id: deployment 106 | uses: actions/deploy-pages@v4 107 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'html-query-v*' 5 | branches: 6 | - '*' 7 | pull_request: 8 | 9 | name: CI 10 | 11 | permissions: 12 | contents: write 13 | checks: write 14 | 15 | jobs: 16 | build_and_test: 17 | name: Rust project 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | matrix: 21 | os: [ ubuntu-latest, macos-latest, windows-latest ] 22 | include: 23 | - os: ubuntu-latest 24 | bin: hq 25 | name: hq-Linux-x86_64.tar.gz 26 | - os: macOS-latest 27 | bin: hq 28 | name: hq-Darwin-x86_64.tar.gz 29 | - os: windows-latest 30 | bin: hq.exe 31 | name: hq-Windows-x86_64.zip 32 | env: 33 | RUST_BACKTRACE: "1" 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Install Rust 37 | id: rust 38 | uses: actions-rust-lang/setup-rust-toolchain@v1 39 | with: 40 | cache: 'false' 41 | cache-on-failure: false 42 | 43 | - run: cargo test 44 | - run: cargo run --bin=hq -- --help 45 | 46 | - name: Build release 47 | if: startsWith(github.ref, 'refs/tags/') 48 | run: cargo build --release 49 | - name: Package 50 | if: startsWith(github.ref, 'refs/tags/') 51 | shell: bash 52 | run: | 53 | strip target/release/${{ matrix.bin }} 54 | cd target/release 55 | if [[ "${{ matrix.os }}" == "windows-latest" ]] 56 | then 57 | 7z a ../../${{ matrix.name }} ${{ matrix.bin }} 58 | else 59 | tar czvf ../../${{ matrix.name }} ${{ matrix.bin }} 60 | fi 61 | cd - 62 | - name: Publish 63 | uses: softprops/action-gh-release@v2 64 | if: startsWith(github.ref, 'refs/tags/') 65 | with: 66 | draft: false 67 | files: 'hq*' 68 | 69 | test_alpine: 70 | name: Test in Alpine 71 | runs-on: ubuntu-latest 72 | container: 73 | image: alpine:latest 74 | steps: 75 | - uses: actions/checkout@v4 76 | - run: apk add libgcc gcc musl-dev bash curl 77 | 78 | - name: Install Rust 79 | id: rust 80 | uses: actions-rust-lang/setup-rust-toolchain@v1 81 | with: 82 | cache: 'false' 83 | cache-on-failure: false 84 | 85 | - run: cargo test 86 | 87 | checks: 88 | name: Checks 89 | runs-on: ubuntu-latest 90 | steps: 91 | - name: Checkout sources 92 | uses: actions/checkout@v4 93 | 94 | - name: Install Rust 95 | id: rust 96 | uses: actions-rust-lang/setup-rust-toolchain@v1 97 | with: 98 | cache: 'false' 99 | cache-on-failure: false 100 | 101 | - name: Run cargo fmt 102 | if: success() || failure() 103 | run: cargo fmt --all -- --check 104 | 105 | - name: Run cargo check 106 | if: success() || failure() 107 | run: cargo check 108 | 109 | - name: Run cargo clippy 110 | if: success() || failure() 111 | run: cargo clippy --all-targets --all-features -- -D warnings 112 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea/ 3 | */dist/ 4 | .DS_Store -------------------------------------------------------------------------------- /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 = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "anstream" 13 | version = "0.6.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 16 | dependencies = [ 17 | "anstyle", 18 | "anstyle-parse", 19 | "anstyle-query", 20 | "anstyle-wincon", 21 | "colorchoice", 22 | "is_terminal_polyfill", 23 | "utf8parse", 24 | ] 25 | 26 | [[package]] 27 | name = "anstyle" 28 | version = "1.0.10" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 31 | 32 | [[package]] 33 | name = "anstyle-parse" 34 | version = "0.2.6" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 37 | dependencies = [ 38 | "utf8parse", 39 | ] 40 | 41 | [[package]] 42 | name = "anstyle-query" 43 | version = "1.1.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 46 | dependencies = [ 47 | "windows-sys", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-wincon" 52 | version = "3.0.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 55 | dependencies = [ 56 | "anstyle", 57 | "once_cell", 58 | "windows-sys", 59 | ] 60 | 61 | [[package]] 62 | name = "anyhow" 63 | version = "1.0.97" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 66 | 67 | [[package]] 68 | name = "async-channel" 69 | version = "1.9.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 72 | dependencies = [ 73 | "concurrent-queue", 74 | "event-listener 2.5.3", 75 | "futures-core", 76 | ] 77 | 78 | [[package]] 79 | name = "async-channel" 80 | version = "2.3.1" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" 83 | dependencies = [ 84 | "concurrent-queue", 85 | "event-listener-strategy", 86 | "futures-core", 87 | "pin-project-lite", 88 | ] 89 | 90 | [[package]] 91 | name = "async-task" 92 | version = "4.7.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 95 | 96 | [[package]] 97 | name = "async-trait" 98 | version = "0.1.88" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 101 | dependencies = [ 102 | "proc-macro2", 103 | "quote", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "atomic-waker" 109 | version = "1.1.2" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 112 | 113 | [[package]] 114 | name = "autocfg" 115 | version = "1.4.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 118 | 119 | [[package]] 120 | name = "bitflags" 121 | version = "2.9.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 124 | dependencies = [ 125 | "serde", 126 | ] 127 | 128 | [[package]] 129 | name = "blocking" 130 | version = "1.6.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 133 | dependencies = [ 134 | "async-channel 2.3.1", 135 | "async-task", 136 | "futures-io", 137 | "futures-lite", 138 | "piper", 139 | ] 140 | 141 | [[package]] 142 | name = "bumpalo" 143 | version = "3.17.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 146 | 147 | [[package]] 148 | name = "byteorder" 149 | version = "1.5.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 152 | 153 | [[package]] 154 | name = "cfg-if" 155 | version = "1.0.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 158 | 159 | [[package]] 160 | name = "clap" 161 | version = "4.5.35" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" 164 | dependencies = [ 165 | "clap_builder", 166 | "clap_derive", 167 | ] 168 | 169 | [[package]] 170 | name = "clap_builder" 171 | version = "4.5.35" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" 174 | dependencies = [ 175 | "anstream", 176 | "anstyle", 177 | "clap_lex", 178 | "strsim", 179 | ] 180 | 181 | [[package]] 182 | name = "clap_derive" 183 | version = "4.5.32" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 186 | dependencies = [ 187 | "heck", 188 | "proc-macro2", 189 | "quote", 190 | "syn", 191 | ] 192 | 193 | [[package]] 194 | name = "clap_lex" 195 | version = "0.7.4" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 198 | 199 | [[package]] 200 | name = "colorchoice" 201 | version = "1.0.3" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 204 | 205 | [[package]] 206 | name = "concurrent-queue" 207 | version = "2.5.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 210 | dependencies = [ 211 | "crossbeam-utils", 212 | ] 213 | 214 | [[package]] 215 | name = "console_error_panic_hook" 216 | version = "0.1.7" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 219 | dependencies = [ 220 | "cfg-if", 221 | "wasm-bindgen", 222 | ] 223 | 224 | [[package]] 225 | name = "constcat" 226 | version = "0.3.1" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" 229 | 230 | [[package]] 231 | name = "crossbeam-utils" 232 | version = "0.8.21" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 235 | 236 | [[package]] 237 | name = "cssparser" 238 | version = "0.34.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" 241 | dependencies = [ 242 | "cssparser-macros", 243 | "dtoa-short", 244 | "itoa", 245 | "phf", 246 | "smallvec", 247 | ] 248 | 249 | [[package]] 250 | name = "cssparser-macros" 251 | version = "0.6.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" 254 | dependencies = [ 255 | "quote", 256 | "syn", 257 | ] 258 | 259 | [[package]] 260 | name = "darling" 261 | version = "0.20.11" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 264 | dependencies = [ 265 | "darling_core", 266 | "darling_macro", 267 | ] 268 | 269 | [[package]] 270 | name = "darling_core" 271 | version = "0.20.11" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 274 | dependencies = [ 275 | "fnv", 276 | "ident_case", 277 | "proc-macro2", 278 | "quote", 279 | "syn", 280 | ] 281 | 282 | [[package]] 283 | name = "darling_macro" 284 | version = "0.20.11" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 287 | dependencies = [ 288 | "darling_core", 289 | "quote", 290 | "syn", 291 | ] 292 | 293 | [[package]] 294 | name = "derive_more" 295 | version = "0.99.19" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" 298 | dependencies = [ 299 | "proc-macro2", 300 | "quote", 301 | "syn", 302 | ] 303 | 304 | [[package]] 305 | name = "dioxus" 306 | version = "0.4.3" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "2d9e3b0725e520250bf23213f996d241cca29cea4360a9bf08a44e0033f8e569" 309 | dependencies = [ 310 | "dioxus-core", 311 | "dioxus-core-macro", 312 | "dioxus-hooks", 313 | "dioxus-hot-reload", 314 | "dioxus-html", 315 | "dioxus-rsx", 316 | ] 317 | 318 | [[package]] 319 | name = "dioxus-core" 320 | version = "0.4.3" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "0f33186615b2e90bceab24a195b3cfad4e0b4d91a33ec44a94845876bfb25c13" 323 | dependencies = [ 324 | "bumpalo", 325 | "futures-channel", 326 | "futures-util", 327 | "longest-increasing-subsequence", 328 | "rustc-hash", 329 | "serde", 330 | "slab", 331 | "smallbox", 332 | "tracing", 333 | ] 334 | 335 | [[package]] 336 | name = "dioxus-core-macro" 337 | version = "0.4.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "21afaccb28587aed0ba98856335912f5ce7052c0aafa74b213829a3b8bfd2345" 340 | dependencies = [ 341 | "constcat", 342 | "dioxus-core", 343 | "dioxus-rsx", 344 | "prettyplease", 345 | "proc-macro2", 346 | "quote", 347 | "syn", 348 | ] 349 | 350 | [[package]] 351 | name = "dioxus-debug-cell" 352 | version = "0.1.1" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" 355 | 356 | [[package]] 357 | name = "dioxus-hooks" 358 | version = "0.4.3" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "5bb23ce82df4fb13e9ddaa01d1469f1f32d683dd4636204bd0a0eaf434b65946" 361 | dependencies = [ 362 | "dioxus-core", 363 | "dioxus-debug-cell", 364 | "futures-channel", 365 | "slab", 366 | "thiserror 1.0.69", 367 | "tracing", 368 | ] 369 | 370 | [[package]] 371 | name = "dioxus-hot-reload" 372 | version = "0.4.3" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b7d8c9e89e866a6b84b8ad696f0ff2f6f6563d2235eb99acc6952f19e516cc09" 375 | dependencies = [ 376 | "dioxus-core", 377 | "dioxus-html", 378 | "dioxus-rsx", 379 | "interprocess-docfix", 380 | "serde", 381 | "serde_json", 382 | ] 383 | 384 | [[package]] 385 | name = "dioxus-html" 386 | version = "0.4.3" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "828a42a2d70688a2412a8538c8b5a5eceadf68f682f899dc4455a0169db39dfd" 389 | dependencies = [ 390 | "async-channel 1.9.0", 391 | "async-trait", 392 | "dioxus-core", 393 | "enumset", 394 | "euclid", 395 | "keyboard-types", 396 | "serde", 397 | "serde-value", 398 | "serde_json", 399 | "serde_repr", 400 | "wasm-bindgen", 401 | "web-sys", 402 | ] 403 | 404 | [[package]] 405 | name = "dioxus-interpreter-js" 406 | version = "0.4.3" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "d9a3115cf9f550a9af88de615c21a15a72dee44230602087dd7b0c5d01f46c37" 409 | dependencies = [ 410 | "js-sys", 411 | "sledgehammer_bindgen", 412 | "sledgehammer_utils", 413 | "wasm-bindgen", 414 | "web-sys", 415 | ] 416 | 417 | [[package]] 418 | name = "dioxus-logger" 419 | version = "0.4.1" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "3d7cbab0b5519060fe9e14b3c21e3f2329b8386cd905618f78c7b929cd00cf54" 422 | dependencies = [ 423 | "log", 424 | "web-sys", 425 | ] 426 | 427 | [[package]] 428 | name = "dioxus-rsx" 429 | version = "0.4.3" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "c974133c7c95497a486d587e40449927711430b308134b9cd374b8d35eceafb3" 432 | dependencies = [ 433 | "dioxus-core", 434 | "proc-macro2", 435 | "quote", 436 | "syn", 437 | ] 438 | 439 | [[package]] 440 | name = "dioxus-web" 441 | version = "0.4.3" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "fafaff75f50035078c2da45441ee61472fd0f335fa15b05170165eaf3479f0df" 444 | dependencies = [ 445 | "async-channel 1.9.0", 446 | "async-trait", 447 | "console_error_panic_hook", 448 | "dioxus-core", 449 | "dioxus-html", 450 | "dioxus-interpreter-js", 451 | "futures-channel", 452 | "futures-util", 453 | "js-sys", 454 | "rustc-hash", 455 | "serde", 456 | "serde-wasm-bindgen", 457 | "serde_json", 458 | "tracing", 459 | "wasm-bindgen", 460 | "wasm-bindgen-futures", 461 | "web-sys", 462 | ] 463 | 464 | [[package]] 465 | name = "dtoa" 466 | version = "1.0.10" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" 469 | 470 | [[package]] 471 | name = "dtoa-short" 472 | version = "0.3.5" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" 475 | dependencies = [ 476 | "dtoa", 477 | ] 478 | 479 | [[package]] 480 | name = "ego-tree" 481 | version = "0.10.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" 484 | 485 | [[package]] 486 | name = "enumset" 487 | version = "1.1.5" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" 490 | dependencies = [ 491 | "enumset_derive", 492 | ] 493 | 494 | [[package]] 495 | name = "enumset_derive" 496 | version = "0.10.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" 499 | dependencies = [ 500 | "darling", 501 | "proc-macro2", 502 | "quote", 503 | "syn", 504 | ] 505 | 506 | [[package]] 507 | name = "equivalent" 508 | version = "1.0.2" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 511 | 512 | [[package]] 513 | name = "euclid" 514 | version = "0.22.11" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" 517 | dependencies = [ 518 | "num-traits", 519 | "serde", 520 | ] 521 | 522 | [[package]] 523 | name = "event-listener" 524 | version = "2.5.3" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 527 | 528 | [[package]] 529 | name = "event-listener" 530 | version = "5.4.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" 533 | dependencies = [ 534 | "concurrent-queue", 535 | "parking", 536 | "pin-project-lite", 537 | ] 538 | 539 | [[package]] 540 | name = "event-listener-strategy" 541 | version = "0.5.4" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 544 | dependencies = [ 545 | "event-listener 5.4.0", 546 | "pin-project-lite", 547 | ] 548 | 549 | [[package]] 550 | name = "fastrand" 551 | version = "2.3.0" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 554 | 555 | [[package]] 556 | name = "fnv" 557 | version = "1.0.7" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 560 | 561 | [[package]] 562 | name = "foldhash" 563 | version = "0.1.5" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 566 | 567 | [[package]] 568 | name = "futf" 569 | version = "0.1.5" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" 572 | dependencies = [ 573 | "mac", 574 | "new_debug_unreachable", 575 | ] 576 | 577 | [[package]] 578 | name = "futures-channel" 579 | version = "0.3.31" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 582 | dependencies = [ 583 | "futures-core", 584 | ] 585 | 586 | [[package]] 587 | name = "futures-core" 588 | version = "0.3.31" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 591 | 592 | [[package]] 593 | name = "futures-io" 594 | version = "0.3.31" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 597 | 598 | [[package]] 599 | name = "futures-lite" 600 | version = "2.6.0" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" 603 | dependencies = [ 604 | "futures-core", 605 | "pin-project-lite", 606 | ] 607 | 608 | [[package]] 609 | name = "futures-macro" 610 | version = "0.3.31" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 613 | dependencies = [ 614 | "proc-macro2", 615 | "quote", 616 | "syn", 617 | ] 618 | 619 | [[package]] 620 | name = "futures-task" 621 | version = "0.3.31" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 624 | 625 | [[package]] 626 | name = "futures-util" 627 | version = "0.3.31" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 630 | dependencies = [ 631 | "futures-core", 632 | "futures-macro", 633 | "futures-task", 634 | "pin-project-lite", 635 | "pin-utils", 636 | "slab", 637 | ] 638 | 639 | [[package]] 640 | name = "fxhash" 641 | version = "0.2.1" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 644 | dependencies = [ 645 | "byteorder", 646 | ] 647 | 648 | [[package]] 649 | name = "getopts" 650 | version = "0.2.21" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 653 | dependencies = [ 654 | "unicode-width", 655 | ] 656 | 657 | [[package]] 658 | name = "getrandom" 659 | version = "0.2.15" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 662 | dependencies = [ 663 | "cfg-if", 664 | "js-sys", 665 | "libc", 666 | "wasi", 667 | "wasm-bindgen", 668 | ] 669 | 670 | [[package]] 671 | name = "hashbrown" 672 | version = "0.15.2" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 675 | dependencies = [ 676 | "allocator-api2", 677 | "equivalent", 678 | "foldhash", 679 | ] 680 | 681 | [[package]] 682 | name = "heck" 683 | version = "0.5.0" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 686 | 687 | [[package]] 688 | name = "html-query" 689 | version = "1.2.2" 690 | dependencies = [ 691 | "anyhow", 692 | "clap", 693 | "html-query-ast", 694 | "html-query-extractor", 695 | "serde_json", 696 | ] 697 | 698 | [[package]] 699 | name = "html-query-ast" 700 | version = "0.2.2" 701 | dependencies = [ 702 | "nom", 703 | "scraper", 704 | ] 705 | 706 | [[package]] 707 | name = "html-query-extractor" 708 | version = "0.2.2" 709 | dependencies = [ 710 | "html-query-ast", 711 | "scraper", 712 | "serde_json", 713 | "thiserror 2.0.12", 714 | ] 715 | 716 | [[package]] 717 | name = "html-query-web-ui" 718 | version = "0.1.0" 719 | dependencies = [ 720 | "console_error_panic_hook", 721 | "dioxus", 722 | "dioxus-logger", 723 | "dioxus-web", 724 | "getrandom", 725 | "html-query-ast", 726 | "html-query-extractor", 727 | "log", 728 | "serde", 729 | "serde_json", 730 | ] 731 | 732 | [[package]] 733 | name = "html5ever" 734 | version = "0.29.1" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" 737 | dependencies = [ 738 | "log", 739 | "mac", 740 | "markup5ever", 741 | "match_token", 742 | ] 743 | 744 | [[package]] 745 | name = "ident_case" 746 | version = "1.0.1" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 749 | 750 | [[package]] 751 | name = "interprocess-docfix" 752 | version = "1.2.2" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" 755 | dependencies = [ 756 | "blocking", 757 | "cfg-if", 758 | "futures-core", 759 | "futures-io", 760 | "intmap", 761 | "libc", 762 | "once_cell", 763 | "rustc_version", 764 | "spinning", 765 | "thiserror 1.0.69", 766 | "to_method", 767 | "winapi", 768 | ] 769 | 770 | [[package]] 771 | name = "intmap" 772 | version = "0.7.1" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" 775 | 776 | [[package]] 777 | name = "is_terminal_polyfill" 778 | version = "1.70.1" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 781 | 782 | [[package]] 783 | name = "itoa" 784 | version = "1.0.15" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 787 | 788 | [[package]] 789 | name = "js-sys" 790 | version = "0.3.77" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 793 | dependencies = [ 794 | "once_cell", 795 | "wasm-bindgen", 796 | ] 797 | 798 | [[package]] 799 | name = "keyboard-types" 800 | version = "0.7.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" 803 | dependencies = [ 804 | "bitflags", 805 | "serde", 806 | "unicode-segmentation", 807 | ] 808 | 809 | [[package]] 810 | name = "libc" 811 | version = "0.2.171" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 814 | 815 | [[package]] 816 | name = "lock_api" 817 | version = "0.4.12" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 820 | dependencies = [ 821 | "autocfg", 822 | "scopeguard", 823 | ] 824 | 825 | [[package]] 826 | name = "log" 827 | version = "0.4.27" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 830 | 831 | [[package]] 832 | name = "longest-increasing-subsequence" 833 | version = "0.1.0" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" 836 | 837 | [[package]] 838 | name = "lru" 839 | version = "0.12.5" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 842 | dependencies = [ 843 | "hashbrown", 844 | ] 845 | 846 | [[package]] 847 | name = "mac" 848 | version = "0.1.1" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 851 | 852 | [[package]] 853 | name = "markup5ever" 854 | version = "0.14.1" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" 857 | dependencies = [ 858 | "log", 859 | "phf", 860 | "phf_codegen", 861 | "string_cache", 862 | "string_cache_codegen", 863 | "tendril", 864 | ] 865 | 866 | [[package]] 867 | name = "match_token" 868 | version = "0.1.0" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" 871 | dependencies = [ 872 | "proc-macro2", 873 | "quote", 874 | "syn", 875 | ] 876 | 877 | [[package]] 878 | name = "memchr" 879 | version = "2.7.4" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 882 | 883 | [[package]] 884 | name = "minimal-lexical" 885 | version = "0.2.1" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 888 | 889 | [[package]] 890 | name = "new_debug_unreachable" 891 | version = "1.0.6" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 894 | 895 | [[package]] 896 | name = "nom" 897 | version = "7.1.3" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 900 | dependencies = [ 901 | "memchr", 902 | "minimal-lexical", 903 | ] 904 | 905 | [[package]] 906 | name = "num-traits" 907 | version = "0.2.19" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 910 | dependencies = [ 911 | "autocfg", 912 | ] 913 | 914 | [[package]] 915 | name = "once_cell" 916 | version = "1.21.3" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 919 | 920 | [[package]] 921 | name = "ordered-float" 922 | version = "2.10.1" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 925 | dependencies = [ 926 | "num-traits", 927 | ] 928 | 929 | [[package]] 930 | name = "parking" 931 | version = "2.2.1" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 934 | 935 | [[package]] 936 | name = "parking_lot" 937 | version = "0.12.3" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 940 | dependencies = [ 941 | "lock_api", 942 | "parking_lot_core", 943 | ] 944 | 945 | [[package]] 946 | name = "parking_lot_core" 947 | version = "0.9.10" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 950 | dependencies = [ 951 | "cfg-if", 952 | "libc", 953 | "redox_syscall", 954 | "smallvec", 955 | "windows-targets", 956 | ] 957 | 958 | [[package]] 959 | name = "phf" 960 | version = "0.11.3" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 963 | dependencies = [ 964 | "phf_macros", 965 | "phf_shared", 966 | ] 967 | 968 | [[package]] 969 | name = "phf_codegen" 970 | version = "0.11.3" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 973 | dependencies = [ 974 | "phf_generator", 975 | "phf_shared", 976 | ] 977 | 978 | [[package]] 979 | name = "phf_generator" 980 | version = "0.11.3" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 983 | dependencies = [ 984 | "phf_shared", 985 | "rand", 986 | ] 987 | 988 | [[package]] 989 | name = "phf_macros" 990 | version = "0.11.3" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" 993 | dependencies = [ 994 | "phf_generator", 995 | "phf_shared", 996 | "proc-macro2", 997 | "quote", 998 | "syn", 999 | ] 1000 | 1001 | [[package]] 1002 | name = "phf_shared" 1003 | version = "0.11.3" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 1006 | dependencies = [ 1007 | "siphasher", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "pin-project-lite" 1012 | version = "0.2.16" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1015 | 1016 | [[package]] 1017 | name = "pin-utils" 1018 | version = "0.1.0" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1021 | 1022 | [[package]] 1023 | name = "piper" 1024 | version = "0.2.4" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 1027 | dependencies = [ 1028 | "atomic-waker", 1029 | "fastrand", 1030 | "futures-io", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "precomputed-hash" 1035 | version = "0.1.1" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 1038 | 1039 | [[package]] 1040 | name = "prettyplease" 1041 | version = "0.2.32" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" 1044 | dependencies = [ 1045 | "proc-macro2", 1046 | "syn", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "proc-macro2" 1051 | version = "1.0.94" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1054 | dependencies = [ 1055 | "unicode-ident", 1056 | ] 1057 | 1058 | [[package]] 1059 | name = "quote" 1060 | version = "1.0.40" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1063 | dependencies = [ 1064 | "proc-macro2", 1065 | ] 1066 | 1067 | [[package]] 1068 | name = "rand" 1069 | version = "0.8.5" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1072 | dependencies = [ 1073 | "rand_core", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "rand_core" 1078 | version = "0.6.4" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1081 | 1082 | [[package]] 1083 | name = "redox_syscall" 1084 | version = "0.5.11" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" 1087 | dependencies = [ 1088 | "bitflags", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "rustc-hash" 1093 | version = "1.1.0" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1096 | 1097 | [[package]] 1098 | name = "rustc_version" 1099 | version = "0.4.1" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1102 | dependencies = [ 1103 | "semver", 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "rustversion" 1108 | version = "1.0.20" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1111 | 1112 | [[package]] 1113 | name = "ryu" 1114 | version = "1.0.20" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1117 | 1118 | [[package]] 1119 | name = "scopeguard" 1120 | version = "1.2.0" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1123 | 1124 | [[package]] 1125 | name = "scraper" 1126 | version = "0.23.1" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "527e65d9d888567588db4c12da1087598d0f6f8b346cc2c5abc91f05fc2dffe2" 1129 | dependencies = [ 1130 | "cssparser", 1131 | "ego-tree", 1132 | "getopts", 1133 | "html5ever", 1134 | "precomputed-hash", 1135 | "selectors", 1136 | "tendril", 1137 | ] 1138 | 1139 | [[package]] 1140 | name = "selectors" 1141 | version = "0.26.0" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" 1144 | dependencies = [ 1145 | "bitflags", 1146 | "cssparser", 1147 | "derive_more", 1148 | "fxhash", 1149 | "log", 1150 | "new_debug_unreachable", 1151 | "phf", 1152 | "phf_codegen", 1153 | "precomputed-hash", 1154 | "servo_arc", 1155 | "smallvec", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "semver" 1160 | version = "1.0.26" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 1163 | 1164 | [[package]] 1165 | name = "serde" 1166 | version = "1.0.219" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1169 | dependencies = [ 1170 | "serde_derive", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "serde-value" 1175 | version = "0.7.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 1178 | dependencies = [ 1179 | "ordered-float", 1180 | "serde", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "serde-wasm-bindgen" 1185 | version = "0.5.0" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" 1188 | dependencies = [ 1189 | "js-sys", 1190 | "serde", 1191 | "wasm-bindgen", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "serde_derive" 1196 | version = "1.0.219" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1199 | dependencies = [ 1200 | "proc-macro2", 1201 | "quote", 1202 | "syn", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "serde_json" 1207 | version = "1.0.140" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1210 | dependencies = [ 1211 | "itoa", 1212 | "memchr", 1213 | "ryu", 1214 | "serde", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "serde_repr" 1219 | version = "0.1.20" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 1222 | dependencies = [ 1223 | "proc-macro2", 1224 | "quote", 1225 | "syn", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "servo_arc" 1230 | version = "0.4.0" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" 1233 | dependencies = [ 1234 | "stable_deref_trait", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "siphasher" 1239 | version = "1.0.1" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 1242 | 1243 | [[package]] 1244 | name = "slab" 1245 | version = "0.4.9" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1248 | dependencies = [ 1249 | "autocfg", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "sledgehammer_bindgen" 1254 | version = "0.2.4" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "c0bc2cf26c12673eee8674b19d56cec04e9b815704c71298eafac61f131f99d7" 1257 | dependencies = [ 1258 | "quote", 1259 | "syn", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "sledgehammer_utils" 1264 | version = "0.2.1" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" 1267 | dependencies = [ 1268 | "lru", 1269 | "once_cell", 1270 | "rustc-hash", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "smallbox" 1275 | version = "0.8.6" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "43d92e0947c1c04c508c9fd39608a1557226141410fd33b5b314d73fa76508d3" 1278 | 1279 | [[package]] 1280 | name = "smallvec" 1281 | version = "1.15.0" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1284 | 1285 | [[package]] 1286 | name = "spinning" 1287 | version = "0.1.0" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" 1290 | dependencies = [ 1291 | "lock_api", 1292 | ] 1293 | 1294 | [[package]] 1295 | name = "stable_deref_trait" 1296 | version = "1.2.0" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1299 | 1300 | [[package]] 1301 | name = "string_cache" 1302 | version = "0.8.9" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" 1305 | dependencies = [ 1306 | "new_debug_unreachable", 1307 | "parking_lot", 1308 | "phf_shared", 1309 | "precomputed-hash", 1310 | "serde", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "string_cache_codegen" 1315 | version = "0.5.4" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" 1318 | dependencies = [ 1319 | "phf_generator", 1320 | "phf_shared", 1321 | "proc-macro2", 1322 | "quote", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "strsim" 1327 | version = "0.11.1" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1330 | 1331 | [[package]] 1332 | name = "syn" 1333 | version = "2.0.100" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1336 | dependencies = [ 1337 | "proc-macro2", 1338 | "quote", 1339 | "unicode-ident", 1340 | ] 1341 | 1342 | [[package]] 1343 | name = "tendril" 1344 | version = "0.4.3" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" 1347 | dependencies = [ 1348 | "futf", 1349 | "mac", 1350 | "utf-8", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "thiserror" 1355 | version = "1.0.69" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1358 | dependencies = [ 1359 | "thiserror-impl 1.0.69", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "thiserror" 1364 | version = "2.0.12" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1367 | dependencies = [ 1368 | "thiserror-impl 2.0.12", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "thiserror-impl" 1373 | version = "1.0.69" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1376 | dependencies = [ 1377 | "proc-macro2", 1378 | "quote", 1379 | "syn", 1380 | ] 1381 | 1382 | [[package]] 1383 | name = "thiserror-impl" 1384 | version = "2.0.12" 1385 | source = "registry+https://github.com/rust-lang/crates.io-index" 1386 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1387 | dependencies = [ 1388 | "proc-macro2", 1389 | "quote", 1390 | "syn", 1391 | ] 1392 | 1393 | [[package]] 1394 | name = "to_method" 1395 | version = "1.1.0" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" 1398 | 1399 | [[package]] 1400 | name = "tracing" 1401 | version = "0.1.41" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1404 | dependencies = [ 1405 | "pin-project-lite", 1406 | "tracing-attributes", 1407 | "tracing-core", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "tracing-attributes" 1412 | version = "0.1.28" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1415 | dependencies = [ 1416 | "proc-macro2", 1417 | "quote", 1418 | "syn", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "tracing-core" 1423 | version = "0.1.33" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1426 | dependencies = [ 1427 | "once_cell", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "unicode-ident" 1432 | version = "1.0.18" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1435 | 1436 | [[package]] 1437 | name = "unicode-segmentation" 1438 | version = "1.12.0" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 1441 | 1442 | [[package]] 1443 | name = "unicode-width" 1444 | version = "0.1.14" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 1447 | 1448 | [[package]] 1449 | name = "utf-8" 1450 | version = "0.7.6" 1451 | source = "registry+https://github.com/rust-lang/crates.io-index" 1452 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1453 | 1454 | [[package]] 1455 | name = "utf8parse" 1456 | version = "0.2.2" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1459 | 1460 | [[package]] 1461 | name = "wasi" 1462 | version = "0.11.0+wasi-snapshot-preview1" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1465 | 1466 | [[package]] 1467 | name = "wasm-bindgen" 1468 | version = "0.2.100" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1471 | dependencies = [ 1472 | "cfg-if", 1473 | "once_cell", 1474 | "rustversion", 1475 | "wasm-bindgen-macro", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "wasm-bindgen-backend" 1480 | version = "0.2.100" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1483 | dependencies = [ 1484 | "bumpalo", 1485 | "log", 1486 | "proc-macro2", 1487 | "quote", 1488 | "syn", 1489 | "wasm-bindgen-shared", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "wasm-bindgen-futures" 1494 | version = "0.4.50" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1497 | dependencies = [ 1498 | "cfg-if", 1499 | "js-sys", 1500 | "once_cell", 1501 | "wasm-bindgen", 1502 | "web-sys", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "wasm-bindgen-macro" 1507 | version = "0.2.100" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1510 | dependencies = [ 1511 | "quote", 1512 | "wasm-bindgen-macro-support", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "wasm-bindgen-macro-support" 1517 | version = "0.2.100" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1520 | dependencies = [ 1521 | "proc-macro2", 1522 | "quote", 1523 | "syn", 1524 | "wasm-bindgen-backend", 1525 | "wasm-bindgen-shared", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "wasm-bindgen-shared" 1530 | version = "0.2.100" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1533 | dependencies = [ 1534 | "unicode-ident", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "web-sys" 1539 | version = "0.3.77" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1542 | dependencies = [ 1543 | "js-sys", 1544 | "wasm-bindgen", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "winapi" 1549 | version = "0.3.9" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1552 | dependencies = [ 1553 | "winapi-i686-pc-windows-gnu", 1554 | "winapi-x86_64-pc-windows-gnu", 1555 | ] 1556 | 1557 | [[package]] 1558 | name = "winapi-i686-pc-windows-gnu" 1559 | version = "0.4.0" 1560 | source = "registry+https://github.com/rust-lang/crates.io-index" 1561 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1562 | 1563 | [[package]] 1564 | name = "winapi-x86_64-pc-windows-gnu" 1565 | version = "0.4.0" 1566 | source = "registry+https://github.com/rust-lang/crates.io-index" 1567 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1568 | 1569 | [[package]] 1570 | name = "windows-sys" 1571 | version = "0.59.0" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1574 | dependencies = [ 1575 | "windows-targets", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "windows-targets" 1580 | version = "0.52.6" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1583 | dependencies = [ 1584 | "windows_aarch64_gnullvm", 1585 | "windows_aarch64_msvc", 1586 | "windows_i686_gnu", 1587 | "windows_i686_gnullvm", 1588 | "windows_i686_msvc", 1589 | "windows_x86_64_gnu", 1590 | "windows_x86_64_gnullvm", 1591 | "windows_x86_64_msvc", 1592 | ] 1593 | 1594 | [[package]] 1595 | name = "windows_aarch64_gnullvm" 1596 | version = "0.52.6" 1597 | source = "registry+https://github.com/rust-lang/crates.io-index" 1598 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1599 | 1600 | [[package]] 1601 | name = "windows_aarch64_msvc" 1602 | version = "0.52.6" 1603 | source = "registry+https://github.com/rust-lang/crates.io-index" 1604 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1605 | 1606 | [[package]] 1607 | name = "windows_i686_gnu" 1608 | version = "0.52.6" 1609 | source = "registry+https://github.com/rust-lang/crates.io-index" 1610 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1611 | 1612 | [[package]] 1613 | name = "windows_i686_gnullvm" 1614 | version = "0.52.6" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1617 | 1618 | [[package]] 1619 | name = "windows_i686_msvc" 1620 | version = "0.52.6" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1623 | 1624 | [[package]] 1625 | name = "windows_x86_64_gnu" 1626 | version = "0.52.6" 1627 | source = "registry+https://github.com/rust-lang/crates.io-index" 1628 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1629 | 1630 | [[package]] 1631 | name = "windows_x86_64_gnullvm" 1632 | version = "0.52.6" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1635 | 1636 | [[package]] 1637 | name = "windows_x86_64_msvc" 1638 | version = "0.52.6" 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" 1640 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1641 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | default-members = ["crates/html-query"] 4 | members = ["crates/html-query", "crates/html-query-ast", "crates/html-query-extractor", "crates/html-query-web-ui"] 5 | 6 | [workspace.dependencies] 7 | scraper = "0.23.1" 8 | -------------------------------------------------------------------------------- /Dioxus.toml: -------------------------------------------------------------------------------- 1 | [application] 2 | 3 | # dioxus project name 4 | name = "html-query-web-ui" 5 | 6 | # default platfrom 7 | # you can also use `dioxus serve/build --platform XXX` to use other platform 8 | # value: web | desktop 9 | default_platform = "web" 10 | 11 | # Web `build` & `serve` dist path 12 | out_dir = "dist" 13 | 14 | # resource (static) file folder 15 | asset_dir = "public" 16 | 17 | sub_package = "html-query-web-ui" 18 | 19 | 20 | [web.app] 21 | 22 | base_path = "html-query" 23 | # HTML title tag content 24 | title = "HTML Query playground" 25 | 26 | [web.watcher] 27 | 28 | index_on_404 = true 29 | 30 | watch_path = ["src"] 31 | 32 | # include `assets` in web platform 33 | [web.resource] 34 | 35 | # CSS style file 36 | style = [] 37 | 38 | # Javascript code file 39 | script = [] 40 | 41 | [web.resource.dev] 42 | 43 | # Javascript code file 44 | # serve: [dev-server] only 45 | script = [] 46 | 47 | [application.plugins] 48 | 49 | available = true 50 | 51 | required = [] 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tom Forbes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hq 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/html-query.svg)](https://crates.io/crates/html-query) 4 | 5 | jq, but for HTML. [Try it in your browser here](https://orf.github.io/html-query/) 6 | 7 | ![](./images/readme-example.gif) 8 | 9 | `hq` reads HTML and converts it into a JSON object based on a series of CSS selectors. The selectors are expressed 10 | in a similar way to JSON, but where the values are CSS selectors. For example: 11 | 12 | ``` 13 | {posts: .athing | [ {title: .titleline > a, url: .titleline > a | @(href)} ] } 14 | ``` 15 | 16 | This will select all `.athing` elements, and it will create an array (`| [{...}]`) of objects for each element selected. 17 | Then for each element it will select the text of the `titleline > a` element, and the `href` attribute (`| @(href)`). 18 | 19 | The end result is the following structure: 20 | 21 | ```json 22 | { 23 | "posts": [ 24 | { 25 | "title": "...", 26 | "url": "..." 27 | } 28 | ] 29 | } 30 | ``` 31 | 32 | ## Install 33 | 34 | `brew install hq`, or `cargo install html-query` 35 | 36 | ## Special query syntax 37 | 38 | ### Text 39 | 40 | `.foo | @text` 41 | 42 | This will select the text content from the first element matching `.foo`. 43 | 44 | ### Selecting attributes 45 | 46 | `.foo | @(href)` 47 | 48 | This will select the `href` attribute from the first element matching `.foo`. 49 | 50 | ### Parents 51 | 52 | `.foo | @parent` 53 | 54 | This will return the parent element from the first element matching `.foo`. 55 | 56 | ### Siblings 57 | 58 | `.foo | @sibling(1)` 59 | 60 | This will return the sibling element from the first element matching `.foo`. 61 | 62 | 63 | ## Examples 64 | 65 | ### Full hacker news story extraction 66 | 67 | ``` 68 | {posts: .athing | [{href: .titleline > a | @(href), title: .titleline > a, meta: @sibling(1) | {user: .hnuser, posted: .age | @(title) }}]} 69 | ``` 70 | 71 | This selects each `.athing` element, extracts the URL from the `href` attribute as well as the title. It then selects 72 | the _sibling_ `.athing` element, and extracts the user and post time from that: 73 | 74 | ```json 75 | { 76 | "posts": [ 77 | { 78 | "title": "...", 79 | "url": "...", 80 | "meta": { 81 | "posted": "...", 82 | "user": "..." 83 | } 84 | } 85 | ] 86 | } 87 | ``` 88 | -------------------------------------------------------------------------------- /crates/html-query-ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html-query-ast" 3 | version = "0.2.2" 4 | edition = "2021" 5 | authors = ["Tom Forbes "] 6 | repository = "https://github.com/orf/hq" 7 | license = "MIT" 8 | description = "Expression parser for hq: jq, but for HTML" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | nom = "=7.1.3" 14 | scraper = {workspace = true} 15 | -------------------------------------------------------------------------------- /crates/html-query-ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | pub use crate::parser::{Action, Expression}; 4 | use nom::error::VerboseError; 5 | use nom::Finish; 6 | pub use parser::format_error; 7 | use std::collections::HashMap; 8 | 9 | pub fn parse_string(input: &str) -> Result, VerboseError<&str>> { 10 | let (_, hashmap) = crate::parser::object(input).finish()?; 11 | Ok(hashmap) 12 | } 13 | -------------------------------------------------------------------------------- /crates/html-query-ast/src/parser.rs: -------------------------------------------------------------------------------- 1 | // This is the first parser I've written using nom. It's based heavily off this one from Nom: 2 | // https://github.com/Geal/nom/blob/3645656644e3ae5074b61cc57e3f62877ada9190/tests/json.rs 3 | 4 | use nom::bytes::complete::{take_while, take_while1}; 5 | use nom::combinator::fail; 6 | use nom::error::{convert_error, VerboseError}; 7 | use nom::multi::many_till; 8 | use nom::sequence::{pair, terminated}; 9 | use nom::{ 10 | branch::alt, 11 | bytes::complete::tag, 12 | character::complete::{char, multispace0}, 13 | combinator::map, 14 | error::ParseError, 15 | multi::separated_list0, 16 | sequence::{delimited, preceded, separated_pair}, 17 | IResult, Parser, 18 | }; 19 | use scraper::Selector; 20 | use std::collections::HashMap; 21 | 22 | #[derive(Debug, PartialEq)] 23 | pub enum Expression { 24 | // .foo | @text 25 | Text, 26 | // .foo | @(href) 27 | Attribute(String), 28 | // @parent 29 | Parent, 30 | // @sibling(1) 31 | Sibling(usize), 32 | // .abc > def 33 | Selector(Selector, String), 34 | } 35 | 36 | #[derive(Debug, PartialEq)] 37 | pub enum Action { 38 | // selector | [{foo: name }] 39 | ForEachChild(HashMap>), 40 | // selector | [.name] 41 | ForEachChildArray(Box), 42 | // selector | {foo: name } 43 | Child(HashMap>), 44 | // .foo > bar | ... 45 | Expression(Expression, Option>), 46 | } 47 | 48 | fn ws<'a, O, E: ParseError<&'a str>, F: Parser<&'a str, O, E>>(f: F) -> impl Parser<&'a str, O, E> { 49 | delimited(multispace0, f, multispace0) 50 | } 51 | 52 | fn alphanum_dash_underscore1(i: &str) -> IResult<&str, &str, VerboseError<&str>> { 53 | take_while1(|c: char| c.is_alphanumeric() || c == '_' || c == '-')(i) 54 | } 55 | 56 | fn object_key(i: &str) -> IResult<&str, &str, VerboseError<&str>> { 57 | terminated(alphanum_dash_underscore1, char(':'))(i) 58 | } 59 | 60 | fn object_key_suffix(i: &str) -> IResult<&str, &str, VerboseError<&str>> { 61 | preceded( 62 | ws(tag(",")), 63 | terminated(alphanum_dash_underscore1, char(':')), 64 | )(i) 65 | } 66 | 67 | fn expression_rhs(i: &str) -> IResult<&str, Expression, VerboseError<&str>> { 68 | // This code is smelly. The issue here is parsing this expression: 69 | // {a: .foo, b: .foo | .bar} 70 | // ^ here 71 | // We need to work out what bit to parse next. `, b: .foo` _could_ be part of 72 | // our selector? i.e `(.foo, b: .foo) | .bar` 73 | // But obviously that's not what we want. So here, we take until we find a `,`, `}` while 74 | // `object_key_suffix` does not match. 75 | let (_, (matches, _)): (_, (Vec<&str>, _)) = many_till( 76 | ws(take_while(|c: char| c != ',' && c != '}')), 77 | alt((ws(object_key_suffix), ws(tag("}")))), 78 | )(i)?; 79 | let rhs = match matches[..] { 80 | [first] if first.contains('|') => { 81 | let (rhs, _) = first.split_once('|').unwrap(); 82 | rhs 83 | } 84 | _ => { 85 | fail::<_, &str, _>(i)?; 86 | unreachable!() 87 | } 88 | }; 89 | let (_, expression) = expression(rhs)?; 90 | let new_rest = &i[rhs.len()..]; 91 | Ok((new_rest, expression)) 92 | } 93 | 94 | fn selector(i: &str) -> IResult<&str, (Selector, &str), VerboseError<&str>> { 95 | let (rest, value) = take_while(|c| !matches!(c, ',' | '}' | ']'))(i)?; 96 | match Selector::parse(value) { 97 | Ok(v) => Ok((rest, (v, value))), 98 | Err(_) => { 99 | fail::<_, &str, _>(i)?; 100 | unreachable!() 101 | } 102 | } 103 | } 104 | 105 | fn expression(i: &str) -> IResult<&str, Expression, VerboseError<&str>> { 106 | alt(( 107 | map(ws(tag("@parent")), |_| Expression::Parent), 108 | map( 109 | delimited(ws(tag("@(")), take_while(|c: char| c != ')'), ws(tag(")"))), 110 | |v: &str| Expression::Attribute(v.to_string()), 111 | ), 112 | map(preceded(ws(tag("@")), tag("text")), |_: &str| { 113 | Expression::Text 114 | }), 115 | map( 116 | delimited( 117 | tag("@sibling("), 118 | ws(take_while1(|c: char| c.is_ascii_digit())), 119 | tag(")"), 120 | ), 121 | |v: &str| Expression::Sibling(v.parse::().unwrap()), 122 | ), 123 | map(selector, |(sel, val)| Expression::Selector(sel, val.into())), 124 | ))(i) 125 | } 126 | 127 | fn object_value(i: &str) -> IResult<&str, Action, VerboseError<&str>> { 128 | alt(( 129 | map(object, |v| { 130 | Action::Child(v.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) 131 | }), 132 | map(delimited(ws(char('[')), object, ws(char(']'))), |v| { 133 | Action::ForEachChild(v.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) 134 | }), 135 | map(delimited(ws(char('[')), object_value, ws(char(']'))), |v| { 136 | Action::ForEachChildArray(v.into()) 137 | }), 138 | map( 139 | separated_pair(expression_rhs, ws(char('|')), object_value), 140 | |v: (Expression, Action)| Action::Expression(v.0, Some(v.1.into())), 141 | ), 142 | map(expression, |v: Expression| Action::Expression(v, None)), 143 | ))(i) 144 | } 145 | 146 | pub fn object(input: &str) -> IResult<&str, HashMap<&str, Action>, VerboseError<&str>> { 147 | map( 148 | delimited( 149 | char('{'), 150 | ws(separated_list0( 151 | ws(char(',')), 152 | pair(object_key, ws(object_value)), 153 | )), 154 | char('}'), 155 | ), 156 | |key_values| key_values.into_iter().collect(), 157 | )(input) 158 | } 159 | 160 | pub fn format_error(input: &str, error: VerboseError<&str>) -> String { 161 | convert_error(input, error) 162 | } 163 | 164 | #[test] 165 | fn test_attribute() { 166 | let expected: HashMap<&str, Action> = vec![( 167 | "foo", 168 | Action::Expression(Expression::Attribute("abc".into()), None), 169 | )] 170 | .into_iter() 171 | .collect(); 172 | 173 | assert_eq!(object("{foo: @(abc)}"), Ok(("", expected))); 174 | } 175 | 176 | #[test] 177 | fn test_nested_attribute() { 178 | let expected: HashMap<&str, Action> = vec![( 179 | "foo", 180 | Action::Expression( 181 | Expression::Selector(Selector::parse(".abc").unwrap(), ".abc ".into()), 182 | Some(Action::Expression(Expression::Attribute("abc".into()), None).into()), 183 | ), 184 | )] 185 | .into_iter() 186 | .collect(); 187 | 188 | assert_eq!(object("{foo: .abc | @(abc)}"), Ok(("", expected))); 189 | } 190 | 191 | #[test] 192 | fn test_nested() { 193 | let expected: HashMap<&str, Action> = vec![( 194 | "foo", 195 | Action::Expression( 196 | Expression::Selector(Selector::parse(".bar").unwrap(), ".bar ".into()), 197 | Some( 198 | Action::ForEachChild( 199 | [( 200 | "baz".to_string(), 201 | Box::new(Action::Expression( 202 | Expression::Attribute("abc".into()), 203 | None, 204 | )), 205 | )] 206 | .into(), 207 | ) 208 | .into(), 209 | ), 210 | ), 211 | )] 212 | .into_iter() 213 | .collect(); 214 | 215 | assert_eq!(object("{foo: .bar | [{baz: @(abc)}]}"), Ok(("", expected))); 216 | } 217 | 218 | #[test] 219 | fn test_array_attribute() { 220 | let expected: HashMap<&str, Action> = vec![( 221 | "foo", 222 | Action::Expression( 223 | Expression::Selector(Selector::parse(".bar").unwrap(), ".bar ".into()), 224 | Some( 225 | Action::ForEachChildArray(Box::new(Action::Expression( 226 | Expression::Attribute("abc".into()), 227 | None, 228 | ))) 229 | .into(), 230 | ), 231 | ), 232 | )] 233 | .into_iter() 234 | .collect(); 235 | 236 | assert_eq!(object("{foo: .bar | [@(abc)]}"), Ok(("", expected))); 237 | } 238 | 239 | #[test] 240 | fn test_array_nested() { 241 | let expected: HashMap<&str, Action> = vec![( 242 | "foo", 243 | Action::Expression( 244 | Expression::Selector(Selector::parse(".bar").unwrap(), ".bar ".into()), 245 | Some( 246 | Action::ForEachChildArray( 247 | Action::Expression( 248 | Expression::Selector(Selector::parse(".lol ").unwrap(), ".lol ".into()), 249 | Some( 250 | Action::Expression( 251 | Expression::Selector(Selector::parse("bar").unwrap(), "bar".into()), 252 | None, 253 | ) 254 | .into(), 255 | ), 256 | ) 257 | .into(), 258 | ) 259 | .into(), 260 | ), 261 | ), 262 | )] 263 | .into_iter() 264 | .collect(); 265 | 266 | assert_eq!(object("{foo: .bar | [.lol | bar]}"), Ok(("", expected))); 267 | } 268 | -------------------------------------------------------------------------------- /crates/html-query-extractor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html-query-extractor" 3 | version = "0.2.2" 4 | edition = "2021" 5 | authors = ["Tom Forbes "] 6 | repository = "https://github.com/orf/hq" 7 | license = "MIT" 8 | description = "HTML extractor for hq: jq, but for HTML" 9 | 10 | [dependencies] 11 | serde_json = "1.0.93" 12 | scraper = {workspace = true} 13 | thiserror = "2.0.12" 14 | html-query-ast = {version= "0.2.2", path= "../html-query-ast" } 15 | -------------------------------------------------------------------------------- /crates/html-query-extractor/src/lib.rs: -------------------------------------------------------------------------------- 1 | use html_query_ast::Action; 2 | use html_query_ast::Expression; 3 | use scraper::{ElementRef, Html}; 4 | use serde_json::{Map, Value}; 5 | use std::collections::HashMap; 6 | 7 | use thiserror::Error; 8 | 9 | #[derive(Error, Debug)] 10 | pub enum ExpressionError { 11 | #[error("Selector `{0} returned no results")] 12 | EmptySelector(String), 13 | 14 | #[error("Unexpected empty root node")] 15 | EmptyRoot, 16 | } 17 | 18 | fn trim_whitespace(input: String) -> String { 19 | input.trim().to_string() 20 | } 21 | 22 | fn handle_expression( 23 | roots: &[ElementRef], 24 | rhs: &Expression, 25 | lhs: &Option>, 26 | ) -> Result { 27 | match rhs { 28 | Expression::Selector(selector, original_selector) => { 29 | let first_root = roots.first().ok_or(ExpressionError::EmptyRoot)?; 30 | let new_roots: Vec<_> = first_root.select(selector).collect(); 31 | let first_new_root = new_roots 32 | .first() 33 | .ok_or_else(|| ExpressionError::EmptySelector(original_selector.clone()))?; 34 | match lhs { 35 | None => Ok(Value::String(trim_whitespace( 36 | first_new_root.text().collect(), 37 | ))), 38 | Some(lhs) => Ok(convert_to_output(lhs, &new_roots)), 39 | } 40 | } 41 | Expression::Attribute(attr) => { 42 | let first_root = roots.first().ok_or(ExpressionError::EmptyRoot)?; 43 | Ok(first_root 44 | .value() 45 | .attr(attr.as_str()) 46 | .map_or(Value::Null, |v| { 47 | Value::String(trim_whitespace(v.to_string())) 48 | })) 49 | } 50 | Expression::Text => { 51 | let first_root = roots.first().ok_or(ExpressionError::EmptyRoot)?; 52 | Ok(Value::String(trim_whitespace(first_root.text().collect()))) 53 | } 54 | Expression::Parent => { 55 | let first_root = roots.first().ok_or(ExpressionError::EmptyRoot)?; 56 | let parent_root = ElementRef::wrap(first_root.parent().unwrap()).unwrap(); 57 | match lhs { 58 | None => handle_expression(&[parent_root], &Expression::Text, &None), 59 | Some(lhs) => Ok(convert_to_output(lhs, &vec![parent_root])), 60 | } 61 | } 62 | Expression::Sibling(idx) => { 63 | let first_root = roots.first().ok_or(ExpressionError::EmptyRoot)?; 64 | let mut next_sibling_elements = first_root 65 | .next_siblings() 66 | .filter(|s| s.value().is_element()); 67 | let chosen_sibling = 68 | ElementRef::wrap(next_sibling_elements.nth(*idx - 1).unwrap()).unwrap(); 69 | match lhs { 70 | None => handle_expression(&[chosen_sibling], &Expression::Text, &None), 71 | Some(lhs) => Ok(convert_to_output(lhs, &vec![chosen_sibling])), 72 | } 73 | } 74 | } 75 | } 76 | 77 | fn convert_to_output(item: &Action, roots: &Vec) -> Value { 78 | match item { 79 | Action::ForEachChild(hashmap) => Value::Array( 80 | roots 81 | .iter() 82 | .map(|root| { 83 | let map = hashmap 84 | .iter() 85 | .map(|(key, value)| (key.clone(), convert_to_output(value, &vec![*root]))) 86 | .collect::>(); 87 | Value::Object(map) 88 | }) 89 | .collect(), 90 | ), 91 | Action::ForEachChildArray(action) => Value::Array( 92 | roots 93 | .iter() 94 | .map(|root| convert_to_output(action, &vec![*root])) 95 | .collect(), 96 | ), 97 | Action::Child(hashmap) => { 98 | let map = hashmap 99 | .iter() 100 | .map(|(key, value)| (key.clone(), convert_to_output(value, roots))) 101 | .collect::>(); 102 | Value::Object(map) 103 | } 104 | Action::Expression(rhs, lhs) => handle_expression(roots, rhs, lhs).unwrap_or(Value::Null), 105 | } 106 | } 107 | 108 | pub fn extract(input: &str, actions: &HashMap<&str, Action>) -> Value { 109 | let fragment = Html::parse_fragment(input); 110 | let root = fragment.root_element(); 111 | let hashmap = actions 112 | .iter() 113 | .map(|(key, value)| (key.to_string(), convert_to_output(value, &vec![root]))) 114 | .collect(); 115 | Value::Object(hashmap) 116 | } 117 | -------------------------------------------------------------------------------- /crates/html-query-web-ui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html-query-web-ui" 3 | authors = ["Tom Forbes "] 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | dioxus = "0.4.3" 11 | dioxus-web = "0.4.3" 12 | getrandom = { version = "0.2", features = ["js"] } 13 | html-query-ast = {version= "0.2.2", path= "../html-query-ast" } 14 | html-query-extractor = {version= "0.2.2", path= "../html-query-extractor" } 15 | serde_json = { version = "1.0.96", default-features = false } 16 | serde = { version = "1.0.193", features = ["derive"] } 17 | 18 | log = "0.4.19" 19 | dioxus-logger = "0.4.1" 20 | console_error_panic_hook = "0.1.7" 21 | 22 | [package.metadata.release] 23 | release = false 24 | -------------------------------------------------------------------------------- /crates/html-query-web-ui/src/examples/examples.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "expression": "{posts: .athing | [ {title: .titleline > a, url: .titleline > a | @(href)} ] }", 4 | "description": "Hacker news titles" 5 | }, 6 | { 7 | "expression": "{posts: .athing | [{href: .titleline > a | @(href), title: .titleline > a, meta: @sibling(1) | {user: .hnuser, posted: .age | @(title) }}]}", 8 | "description": "Full hacker news information" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /crates/html-query-web-ui/src/examples/hn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hacker News
5 | 11 | 165 |
6 | 10 |
Hacker News 7 | new | past | comments | ask | show | jobs | submit 8 | login 9 |
12 | 13 | 16 | 17 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 | 53 | 56 | 57 | 58 | 61 | 62 | 63 | 66 | 67 | 68 | 71 | 72 | 73 | 76 | 77 | 78 | 81 | 82 | 83 | 86 | 87 | 88 | 91 | 92 | 93 | 96 | 97 | 98 | 101 | 102 | 103 | 106 | 107 | 108 | 111 | 112 | 113 | 116 | 117 | 118 | 121 | 122 | 123 | 125 | 126 | 127 | 130 | 131 | 132 | 135 | 136 | 137 | 140 | 141 | 142 | 145 | 146 | 147 | 150 | 151 | 152 | 155 | 156 | 157 | 160 | 161 | 162 | 163 |
1. 91% of ML Models Degrade over Time (nannyml.com)
14 | 177 points by santiviquez 3 hours ago | hide | 107 comments 15 |
2. Stop whining about “The EU Cookie Policy” and improve your ways (wildeboer.net)
19 | 35 points by rapnie 45 minutes ago | hide | 31 comments 20 |
3. Zig Build System (liujiacai.net)
24 | 24 points by SerCe 1 hour ago | hide | 2 comments 25 |
4. Animated Drawings (fairanimateddrawings.com)
29 | 584 points by jonbaer 13 hours ago | hide | 49 comments 30 |
5. GNU toolchain for RISC-V including GCC (github.com/riscv-collab)
34 | 84 points by teleforce 7 hours ago | hide | 15 comments 35 |
6. Exoticsilicon.com – Replying to comments about our web page design (exoticsilicon.com)
39 | 5 points by nhanb 25 minutes ago | hide | discuss 40 |
7. Building LLM Applications for Production (huyenchip.com)
44 | 80 points by tim_sw 6 hours ago | hide | 37 comments 45 |
8. Sorting waste and recyclables with a fleet of robots (googleblog.com)
49 | 203 points by mfiguiere 13 hours ago | hide | 71 comments 50 |
9. End-of-Life Dreams (commonwealmagazine.org)
54 | 226 points by nopg 13 hours ago | hide | 128 comments 55 |
10. Primitive Asgard cells show life on the brink of complexity (quantamagazine.org)
59 | 56 points by nsoonhui 7 hours ago | hide | 4 comments 60 |
11. SQL:2023 is finished: Here is what's new (eisentraut.org)
64 | 206 points by petalmind 12 hours ago | hide | 106 comments 65 |
12. Vast New Stores of Water Reported on the Moon (ieee.org)
69 | 92 points by mfiguiere 5 hours ago | hide | 88 comments 70 |
13. Zylin ZPU: 32 bit CPU with GCC toolchain (github.com/zylin)
74 | 67 points by panic 9 hours ago | hide | 14 comments 75 |
14. Layout 2013 and Layout 2020 (servo.org)
79 | 117 points by jerheinze 7 hours ago | hide | 74 comments 80 |
15. Choose Your Weapon: Survival Strategies for Depressed AI Academics (arxiv.org)
84 | 34 points by bundie 9 hours ago | hide | 17 comments 85 |
16. Improving Tailscale via Apple’s open source (tailscale.dev)
89 | 401 points by mfiguiere 16 hours ago | hide | 57 comments 90 |
17. Shifting the Balance of Cybersecurity Risk: Security-by-Design and -Default [pdf] (cisa.gov)
94 | 64 points by dfern 12 hours ago | hide | 18 comments 95 |
18. Ask HN: Technology/Creative books and games for my daughter (7 years)
99 | 14 points by rrr83 2 hours ago | hide | 8 comments 100 |
19. Requiem for a Great Cat (newyorker.com)
104 | 62 points by Hooke 11 hours ago | hide | 36 comments 105 |
20. The Rules of the Games in Tudor England (laphamsquarterly.org)
109 | 6 points by Petiver 3 hours ago | hide | 1 comment 110 |
21. The Odd Knight of the Cinnamon Shops (tabletmag.com)
114 | 7 points by lermontov 3 hours ago | hide | discuss 115 |
22. Searching for the River of Wind (nautil.us)
119 | 3 points by dnetesn 3 hours ago | hide | discuss 120 |
23. Atlas (YC W22) Is Hiring for a Demand Gen Manager (International) (ycombinator.com)
124 | 8 hours ago | hide
24. Camus's New York Diary (1946) (theparisreview.org)
128 | 58 points by amanuensis 12 hours ago | hide | 27 comments 129 |
25. Consent-O-Matic: Automatic cookie management (au.dk)
133 | 162 points by DerekBickerton 12 hours ago | hide | 127 comments 134 |
26. GPT Unicorn: A Daily Exploration of GPT-4's Image Generation Capabilities (adamkdean.co.uk)
138 | 135 points by imdsm 13 hours ago | hide | 83 comments 139 |
27. When you combine two things that are close, but not the same (twitter.com/id_aa_carmack)
143 | 94 points by tosh 11 hours ago | hide | 71 comments 144 |
28. Don’t record your social life on an append-only social network (2022) (ctrl.blog)
148 | 142 points by cristoperb 14 hours ago | hide | 113 comments 149 |
29. Show HN: Google Analytics alternative with the most generous free tier (beamanalytics.io)
153 | 112 points by flurly 14 hours ago | hide | 67 comments 154 |
30. I hope you will never see this letter (1961) (lettersofnote.com)
158 | 99 points by doat 12 hours ago | hide | 68 comments 159 |
164 |

166 |
Applications are open for YC Summer 2023

167 |
Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

168 |
Search:
169 | 170 | 171 | -------------------------------------------------------------------------------- /crates/html-query-web-ui/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | // import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types 4 | use dioxus::prelude::*; 5 | use html_query_ast::parse_string; 6 | use html_query_extractor::extract; 7 | use log::LevelFilter; 8 | use serde::Deserialize; 9 | 10 | fn main() { 11 | // launch the web app 12 | dioxus_logger::init(LevelFilter::Info).expect("failed to init logger"); 13 | console_error_panic_hook::set_once(); 14 | 15 | log::info!("starting app"); 16 | dioxus_web::launch(app); 17 | } 18 | 19 | #[derive(Deserialize)] 20 | pub struct Example { 21 | expression: &'static str, 22 | description: &'static str, 23 | } 24 | 25 | static HN_CONTENT: &str = include_str!("examples/hn.html"); 26 | 27 | type ExampleTuple<'a> = (&'static str, &'static str); 28 | 29 | #[inline_props] 30 | fn Examples<'a>(cx: Scope<'a>, on_input: EventHandler<'a, ExampleTuple<'a>>) -> Element { 31 | let examples: Vec = 32 | serde_json::from_str(include_str!("examples/examples.json")).unwrap(); 33 | 34 | let buttons = examples.into_iter().map(|ex| { 35 | rsx!( 36 | button { 37 | class: "button", 38 | onclick: move |_event| { on_input.call((HN_CONTENT, ex.expression)) }, 39 | "{ex.description}" 40 | } 41 | ) 42 | }); 43 | 44 | cx.render(rsx! { 45 | div { class: "block", 46 | div { class: "buttons", buttons } 47 | } 48 | }) 49 | } 50 | 51 | fn app(cx: Scope) -> Element { 52 | let expression = use_state(cx, || "{}".to_string()); 53 | let parsed = parse_string(expression); 54 | let html = use_state(cx, || "foo".to_string()); 55 | 56 | let output = match &parsed { 57 | Ok(parsed) => { 58 | let output = extract(html, parsed); 59 | serde_json::to_string_pretty(&output).unwrap() 60 | } 61 | Err(_) => "".to_string(), 62 | }; 63 | 64 | cx.render(rsx! { 65 | p { class: "title is-1", "hq: jq, but for HTML" } 66 | // p { 67 | // class: "subtitle is-3", 68 | // "test" 69 | // } 70 | 71 | Examples { 72 | on_input: move |event: ExampleTuple| { 73 | let (example_content, example_expression) = event; 74 | html.set(example_content.to_owned()); 75 | expression.set(example_expression.to_owned()); 76 | } 77 | } 78 | 79 | div { class: "block", 80 | textarea { 81 | // we tell the component what to render 82 | value: "{expression}", 83 | class: "input is-large", 84 | // and what to do when the value changes 85 | oninput: move |evt| expression.set(evt.value.clone()) 86 | } 87 | } 88 | 89 | div { class: "columns", 90 | div { class: "column", textarea { 91 | value: "{html}", 92 | class: "textarea", 93 | oninput: move |evt| html.set(evt.value.clone()) 94 | } } 95 | 96 | div { class: "column", 97 | pre { style: "white-space: pre-wrap;", code { "{output}" } } 98 | } 99 | } 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /crates/html-query/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html-query" 3 | version = "1.2.2" 4 | edition = "2021" 5 | authors = ["Tom Forbes "] 6 | repository = "https://github.com/orf/hq" 7 | license = "MIT" 8 | description = "jq, but for HTML" 9 | readme = "../../README.md" 10 | 11 | [dependencies] 12 | clap = { version = "4.4.11", features = ["derive"] } 13 | anyhow = "1.0.70" 14 | serde_json = "1.0.93" 15 | html-query-ast = {version= "0.2.2", path= "../html-query-ast" } 16 | html-query-extractor = {version= "0.2.2", path= "../html-query-extractor" } 17 | 18 | [[bin]] 19 | name = "hq" 20 | path = "src/main.rs" 21 | -------------------------------------------------------------------------------- /crates/html-query/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use html_query_ast::parse_string; 3 | use html_query_extractor::extract; 4 | 5 | use std::io::Read; 6 | use std::path::PathBuf; 7 | use std::{fs, io}; 8 | 9 | /// jq, but for HTML 10 | #[derive(Parser, Debug)] 11 | #[command(author, version, about, long_about = None)] 12 | struct Args { 13 | /// Query describing data to extract 14 | #[arg(index = 1)] 15 | query: String, 16 | 17 | /// Input file. Uses stdin if not given. 18 | #[arg(index = 2)] 19 | input_file: Option, 20 | } 21 | 22 | fn main() -> anyhow::Result<()> { 23 | let args: Args = Args::parse(); 24 | 25 | match parse_string(&args.query) { 26 | Ok(res) => { 27 | let input_str = match args.input_file { 28 | None => { 29 | let mut buf = String::new(); 30 | io::stdin().lock().read_to_string(&mut buf)?; 31 | buf 32 | } 33 | Some(path) => fs::read_to_string(path)?, 34 | }; 35 | let output = extract(input_str.as_str(), &res); 36 | serde_json::to_writer(std::io::stdout().lock(), &output)?; 37 | println!(); 38 | } 39 | Err(e) => { 40 | eprintln!( 41 | "Error parsing:\n{}", 42 | html_query_ast::format_error(args.query.as_str(), e) 43 | ); 44 | } 45 | } 46 | Ok(()) 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | // Note this useful idiom: importing names from outer (for mod tests) scope. 52 | use super::*; 53 | 54 | #[test] 55 | fn test_parse_whitespace() { 56 | let html = include_str!("tests/whitespace.html"); 57 | let expr = parse_string("{foo: h1}").unwrap(); 58 | assert_eq!( 59 | extract(html, &expr), 60 | serde_json::json!({ 61 | "foo": "This is some whitespace" 62 | }) 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/html-query/src/tests/whitespace.html: -------------------------------------------------------------------------------- 1 |

2 | This is some whitespace 3 |

4 | -------------------------------------------------------------------------------- /images/readme-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orf/html-query/9bba3d2553f4a3bac5db69aa29707a819c5b35bd/images/readme-example.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {app_title} 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 19 | {style_include} 20 | 21 | 22 |
23 |
24 |
25 |
26 |
27 | 28 | {script_include} 29 | 30 | 31 | --------------------------------------------------------------------------------