├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── install.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── src ├── assets.rs ├── bat.version ├── cmd.rs ├── config.rs ├── edit.rs ├── error.rs ├── fmt.rs ├── main.rs ├── manifest.rs ├── opts.rs ├── unparse.rs └── version.rs └── tests ├── Cargo.toml ├── lib.expand.rs └── lib.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dtolnay 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: [cron: "40 1 * * *"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | env: 13 | RUSTFLAGS: -Dwarnings 14 | 15 | jobs: 16 | pre_ci: 17 | uses: dtolnay/.github/.github/workflows/pre_ci.yml@master 18 | 19 | test: 20 | name: ${{matrix.name || format('Rust {0}', matrix.rust)}} 21 | needs: pre_ci 22 | if: needs.pre_ci.outputs.continue 23 | runs-on: ${{matrix.os}}-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | rust: [nightly, beta, stable, 1.81.0] 28 | os: [ubuntu] 29 | include: 30 | - name: macOS 31 | os: macos 32 | rust: nightly 33 | - name: Windows (gnu) 34 | os: windows 35 | rust: nightly-x86_64-pc-windows-gnu 36 | - name: Windows (msvc) 37 | os: windows 38 | rust: nightly-x86_64-pc-windows-msvc 39 | timeout-minutes: 45 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: dtolnay/rust-toolchain@master 43 | with: 44 | toolchain: ${{matrix.rust}} 45 | - name: Enable type layout randomization 46 | run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV 47 | if: matrix.rust == 'nightly' 48 | shell: bash 49 | - name: Ignore Windows linker warning 50 | run: echo RUSTFLAGS=${RUSTFLAGS}\ -Alinker_messages >> $GITHUB_ENV 51 | if: matrix.rust == 'nightly-x86_64-pc-windows-gnu' 52 | shell: bash 53 | - run: cargo check --locked 54 | - run: cargo update 55 | - run: cargo check 56 | - run: cargo test 57 | if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' 58 | - run: cargo run -- expand --manifest-path tests/Cargo.toml > expand.rs && diff tests/lib.expand.rs expand.rs 59 | if: startsWith(matrix.rust, 'nightly') 60 | - uses: actions/upload-artifact@v4 61 | if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' && always() 62 | with: 63 | name: Cargo.lock 64 | path: Cargo.lock 65 | continue-on-error: true 66 | 67 | clippy: 68 | name: Clippy 69 | runs-on: ubuntu-latest 70 | if: github.event_name != 'pull_request' 71 | timeout-minutes: 45 72 | steps: 73 | - uses: actions/checkout@v4 74 | - uses: dtolnay/rust-toolchain@clippy 75 | - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic 76 | 77 | outdated: 78 | name: Outdated 79 | runs-on: ubuntu-latest 80 | if: github.event_name != 'pull_request' 81 | timeout-minutes: 45 82 | steps: 83 | - uses: actions/checkout@v4 84 | - uses: dtolnay/rust-toolchain@stable 85 | - uses: dtolnay/install@cargo-outdated 86 | - run: cargo tree --package bat --depth 0 | grep "^bat v$(cat src/bat.version)$" 87 | - run: cargo update 88 | - run: cargo outdated --workspace --exit-code 1 89 | -------------------------------------------------------------------------------- /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: [cron: "40 1 * * *"] 6 | push: {tags: ['*']} 7 | 8 | permissions: {} 9 | 10 | env: 11 | RUSTFLAGS: -Dwarnings 12 | 13 | jobs: 14 | install: 15 | name: Install 16 | uses: dtolnay/.github/.github/workflows/check_install.yml@master 17 | with: 18 | crate: cargo-expand 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | expand.rs 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "1.1.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "ansi_colours" 22 | version = "1.2.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "14eec43e0298190790f41679fe69ef7a829d2a2ddd78c8c00339e84710e435fe" 25 | dependencies = [ 26 | "rgb", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.6.18" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is_terminal_polyfill", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.10" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.6" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.1.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 64 | dependencies = [ 65 | "windows-sys 0.59.0", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "3.0.8" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" 73 | dependencies = [ 74 | "anstyle", 75 | "once_cell_polyfill", 76 | "windows-sys 0.59.0", 77 | ] 78 | 79 | [[package]] 80 | name = "anyhow" 81 | version = "1.0.98" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 84 | 85 | [[package]] 86 | name = "autocfg" 87 | version = "1.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 90 | 91 | [[package]] 92 | name = "base64" 93 | version = "0.22.1" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 96 | 97 | [[package]] 98 | name = "bat" 99 | version = "0.25.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "2ab792c2ad113a666f08856c88cdec0a62d732559b1f3982eedf0142571e669a" 102 | dependencies = [ 103 | "ansi_colours", 104 | "anyhow", 105 | "bincode", 106 | "bytesize", 107 | "clircle", 108 | "console", 109 | "content_inspector", 110 | "encoding_rs", 111 | "flate2", 112 | "globset", 113 | "grep-cli", 114 | "home", 115 | "indexmap", 116 | "itertools", 117 | "nu-ansi-term", 118 | "once_cell", 119 | "path_abs", 120 | "plist", 121 | "regex", 122 | "semver", 123 | "serde", 124 | "serde_derive", 125 | "serde_with", 126 | "serde_yaml", 127 | "shell-words", 128 | "syntect", 129 | "terminal-colorsaurus", 130 | "thiserror", 131 | "toml", 132 | "unicode-width 0.1.14", 133 | "walkdir", 134 | ] 135 | 136 | [[package]] 137 | name = "bincode" 138 | version = "1.3.3" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 141 | dependencies = [ 142 | "serde", 143 | ] 144 | 145 | [[package]] 146 | name = "bit-set" 147 | version = "0.5.3" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 150 | dependencies = [ 151 | "bit-vec", 152 | ] 153 | 154 | [[package]] 155 | name = "bit-vec" 156 | version = "0.6.3" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 159 | 160 | [[package]] 161 | name = "bitflags" 162 | version = "1.3.2" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 165 | 166 | [[package]] 167 | name = "bitflags" 168 | version = "2.9.1" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 171 | 172 | [[package]] 173 | name = "bstr" 174 | version = "1.12.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 177 | dependencies = [ 178 | "memchr", 179 | "regex-automata", 180 | "serde", 181 | ] 182 | 183 | [[package]] 184 | name = "bytemuck" 185 | version = "1.23.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" 188 | 189 | [[package]] 190 | name = "bytesize" 191 | version = "1.3.3" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" 194 | 195 | [[package]] 196 | name = "cargo-expand" 197 | version = "1.0.108" 198 | dependencies = [ 199 | "bat", 200 | "cargo-subcommand-metadata", 201 | "clap", 202 | "clap-cargo", 203 | "console", 204 | "fs-err", 205 | "home", 206 | "prettyplease", 207 | "proc-macro2", 208 | "quote", 209 | "semver", 210 | "serde", 211 | "shlex", 212 | "syn", 213 | "syn-select", 214 | "tempfile", 215 | "termcolor", 216 | "toml", 217 | "toolchain_find", 218 | "windows-sys 0.59.0", 219 | ] 220 | 221 | [[package]] 222 | name = "cargo-expand-test" 223 | version = "0.0.0" 224 | 225 | [[package]] 226 | name = "cargo-subcommand-metadata" 227 | version = "0.1.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "a33d3b80a8db16c4ad7676653766a8e59b5f95443c8823cb7cff587b90cb91ba" 230 | 231 | [[package]] 232 | name = "cfg-if" 233 | version = "1.0.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 236 | 237 | [[package]] 238 | name = "clap" 239 | version = "4.5.39" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" 242 | dependencies = [ 243 | "clap_builder", 244 | "clap_derive", 245 | ] 246 | 247 | [[package]] 248 | name = "clap-cargo" 249 | version = "0.15.2" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "d546f0e84ff2bfa4da1ce9b54be42285767ba39c688572ca32412a09a73851e5" 252 | dependencies = [ 253 | "anstyle", 254 | "clap", 255 | ] 256 | 257 | [[package]] 258 | name = "clap_builder" 259 | version = "4.5.39" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" 262 | dependencies = [ 263 | "anstream", 264 | "anstyle", 265 | "clap_lex", 266 | "strsim", 267 | ] 268 | 269 | [[package]] 270 | name = "clap_derive" 271 | version = "4.5.32" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 274 | dependencies = [ 275 | "heck", 276 | "proc-macro2", 277 | "quote", 278 | "syn", 279 | ] 280 | 281 | [[package]] 282 | name = "clap_lex" 283 | version = "0.7.4" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 286 | 287 | [[package]] 288 | name = "clircle" 289 | version = "0.6.1" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "7d9334f725b46fb9bed8580b9b47a932587e044fadb344ed7fa98774b067ac1a" 292 | dependencies = [ 293 | "cfg-if", 294 | "windows", 295 | ] 296 | 297 | [[package]] 298 | name = "colorchoice" 299 | version = "1.0.3" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 302 | 303 | [[package]] 304 | name = "console" 305 | version = "0.15.11" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" 308 | dependencies = [ 309 | "encode_unicode", 310 | "libc", 311 | "once_cell", 312 | "unicode-width 0.2.0", 313 | "windows-sys 0.59.0", 314 | ] 315 | 316 | [[package]] 317 | name = "content_inspector" 318 | version = "0.2.4" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" 321 | dependencies = [ 322 | "memchr", 323 | ] 324 | 325 | [[package]] 326 | name = "crc32fast" 327 | version = "1.4.2" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 330 | dependencies = [ 331 | "cfg-if", 332 | ] 333 | 334 | [[package]] 335 | name = "darling" 336 | version = "0.20.11" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 339 | dependencies = [ 340 | "darling_core", 341 | "darling_macro", 342 | ] 343 | 344 | [[package]] 345 | name = "darling_core" 346 | version = "0.20.11" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 349 | dependencies = [ 350 | "fnv", 351 | "ident_case", 352 | "proc-macro2", 353 | "quote", 354 | "strsim", 355 | "syn", 356 | ] 357 | 358 | [[package]] 359 | name = "darling_macro" 360 | version = "0.20.11" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 363 | dependencies = [ 364 | "darling_core", 365 | "quote", 366 | "syn", 367 | ] 368 | 369 | [[package]] 370 | name = "deranged" 371 | version = "0.4.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 374 | dependencies = [ 375 | "powerfmt", 376 | ] 377 | 378 | [[package]] 379 | name = "either" 380 | version = "1.15.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 383 | 384 | [[package]] 385 | name = "encode_unicode" 386 | version = "1.0.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 389 | 390 | [[package]] 391 | name = "encoding_rs" 392 | version = "0.8.35" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 395 | dependencies = [ 396 | "cfg-if", 397 | ] 398 | 399 | [[package]] 400 | name = "equivalent" 401 | version = "1.0.2" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 404 | 405 | [[package]] 406 | name = "errno" 407 | version = "0.3.12" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 410 | dependencies = [ 411 | "libc", 412 | "windows-sys 0.59.0", 413 | ] 414 | 415 | [[package]] 416 | name = "fancy-regex" 417 | version = "0.11.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" 420 | dependencies = [ 421 | "bit-set", 422 | "regex", 423 | ] 424 | 425 | [[package]] 426 | name = "fastrand" 427 | version = "2.3.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 430 | 431 | [[package]] 432 | name = "flate2" 433 | version = "1.1.1" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" 436 | dependencies = [ 437 | "crc32fast", 438 | "miniz_oxide", 439 | ] 440 | 441 | [[package]] 442 | name = "fnv" 443 | version = "1.0.7" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 446 | 447 | [[package]] 448 | name = "fs-err" 449 | version = "3.1.0" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" 452 | dependencies = [ 453 | "autocfg", 454 | ] 455 | 456 | [[package]] 457 | name = "getrandom" 458 | version = "0.3.3" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 461 | dependencies = [ 462 | "cfg-if", 463 | "libc", 464 | "r-efi", 465 | "wasi 0.14.2+wasi-0.2.4", 466 | ] 467 | 468 | [[package]] 469 | name = "globset" 470 | version = "0.4.16" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 473 | dependencies = [ 474 | "aho-corasick", 475 | "bstr", 476 | "log", 477 | "regex-automata", 478 | "regex-syntax", 479 | ] 480 | 481 | [[package]] 482 | name = "grep-cli" 483 | version = "0.1.11" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "47f1288f0e06f279f84926fa4c17e3fcd2a22b357927a82f2777f7be26e4cec0" 486 | dependencies = [ 487 | "bstr", 488 | "globset", 489 | "libc", 490 | "log", 491 | "termcolor", 492 | "winapi-util", 493 | ] 494 | 495 | [[package]] 496 | name = "hashbrown" 497 | version = "0.15.3" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 500 | 501 | [[package]] 502 | name = "heck" 503 | version = "0.5.0" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 506 | 507 | [[package]] 508 | name = "home" 509 | version = "0.5.11" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 512 | dependencies = [ 513 | "windows-sys 0.59.0", 514 | ] 515 | 516 | [[package]] 517 | name = "ident_case" 518 | version = "1.0.1" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 521 | 522 | [[package]] 523 | name = "indexmap" 524 | version = "2.9.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 527 | dependencies = [ 528 | "equivalent", 529 | "hashbrown", 530 | "serde", 531 | ] 532 | 533 | [[package]] 534 | name = "is_terminal_polyfill" 535 | version = "1.70.1" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 538 | 539 | [[package]] 540 | name = "itertools" 541 | version = "0.13.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 544 | dependencies = [ 545 | "either", 546 | ] 547 | 548 | [[package]] 549 | name = "itoa" 550 | version = "1.0.15" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 553 | 554 | [[package]] 555 | name = "libc" 556 | version = "0.2.172" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 559 | 560 | [[package]] 561 | name = "linux-raw-sys" 562 | version = "0.9.4" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 565 | 566 | [[package]] 567 | name = "log" 568 | version = "0.4.27" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 571 | 572 | [[package]] 573 | name = "memchr" 574 | version = "2.7.4" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 577 | 578 | [[package]] 579 | name = "miniz_oxide" 580 | version = "0.8.8" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 583 | dependencies = [ 584 | "adler2", 585 | ] 586 | 587 | [[package]] 588 | name = "mio" 589 | version = "1.0.4" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 592 | dependencies = [ 593 | "libc", 594 | "wasi 0.11.0+wasi-snapshot-preview1", 595 | "windows-sys 0.59.0", 596 | ] 597 | 598 | [[package]] 599 | name = "nu-ansi-term" 600 | version = "0.50.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" 603 | dependencies = [ 604 | "windows-sys 0.52.0", 605 | ] 606 | 607 | [[package]] 608 | name = "num-conv" 609 | version = "0.1.0" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 612 | 613 | [[package]] 614 | name = "once_cell" 615 | version = "1.21.3" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 618 | 619 | [[package]] 620 | name = "once_cell_polyfill" 621 | version = "1.70.1" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 624 | 625 | [[package]] 626 | name = "path_abs" 627 | version = "0.5.1" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "05ef02f6342ac01d8a93b65f96db53fe68a92a15f41144f97fb00a9e669633c3" 630 | dependencies = [ 631 | "std_prelude", 632 | ] 633 | 634 | [[package]] 635 | name = "plist" 636 | version = "1.7.1" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" 639 | dependencies = [ 640 | "base64", 641 | "indexmap", 642 | "quick-xml", 643 | "serde", 644 | "time", 645 | ] 646 | 647 | [[package]] 648 | name = "powerfmt" 649 | version = "0.2.0" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 652 | 653 | [[package]] 654 | name = "prettyplease" 655 | version = "0.2.33" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" 658 | dependencies = [ 659 | "proc-macro2", 660 | "syn", 661 | ] 662 | 663 | [[package]] 664 | name = "proc-macro2" 665 | version = "1.0.95" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 668 | dependencies = [ 669 | "unicode-ident", 670 | ] 671 | 672 | [[package]] 673 | name = "quick-xml" 674 | version = "0.32.0" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" 677 | dependencies = [ 678 | "memchr", 679 | ] 680 | 681 | [[package]] 682 | name = "quote" 683 | version = "1.0.40" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 686 | dependencies = [ 687 | "proc-macro2", 688 | ] 689 | 690 | [[package]] 691 | name = "r-efi" 692 | version = "5.2.0" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 695 | 696 | [[package]] 697 | name = "regex" 698 | version = "1.11.1" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 701 | dependencies = [ 702 | "aho-corasick", 703 | "memchr", 704 | "regex-automata", 705 | "regex-syntax", 706 | ] 707 | 708 | [[package]] 709 | name = "regex-automata" 710 | version = "0.4.9" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 713 | dependencies = [ 714 | "aho-corasick", 715 | "memchr", 716 | "regex-syntax", 717 | ] 718 | 719 | [[package]] 720 | name = "regex-syntax" 721 | version = "0.8.5" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 724 | 725 | [[package]] 726 | name = "rgb" 727 | version = "0.8.50" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" 730 | dependencies = [ 731 | "bytemuck", 732 | ] 733 | 734 | [[package]] 735 | name = "rustix" 736 | version = "1.0.7" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 739 | dependencies = [ 740 | "bitflags 2.9.1", 741 | "errno", 742 | "libc", 743 | "linux-raw-sys", 744 | "windows-sys 0.59.0", 745 | ] 746 | 747 | [[package]] 748 | name = "ryu" 749 | version = "1.0.20" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 752 | 753 | [[package]] 754 | name = "same-file" 755 | version = "1.0.6" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 758 | dependencies = [ 759 | "winapi-util", 760 | ] 761 | 762 | [[package]] 763 | name = "semver" 764 | version = "1.0.26" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 767 | 768 | [[package]] 769 | name = "serde" 770 | version = "1.0.219" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 773 | dependencies = [ 774 | "serde_derive", 775 | ] 776 | 777 | [[package]] 778 | name = "serde_derive" 779 | version = "1.0.219" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 782 | dependencies = [ 783 | "proc-macro2", 784 | "quote", 785 | "syn", 786 | ] 787 | 788 | [[package]] 789 | name = "serde_json" 790 | version = "1.0.140" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 793 | dependencies = [ 794 | "itoa", 795 | "memchr", 796 | "ryu", 797 | "serde", 798 | ] 799 | 800 | [[package]] 801 | name = "serde_spanned" 802 | version = "0.6.8" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 805 | dependencies = [ 806 | "serde", 807 | ] 808 | 809 | [[package]] 810 | name = "serde_with" 811 | version = "3.12.0" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" 814 | dependencies = [ 815 | "serde", 816 | "serde_derive", 817 | "serde_with_macros", 818 | ] 819 | 820 | [[package]] 821 | name = "serde_with_macros" 822 | version = "3.12.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" 825 | dependencies = [ 826 | "darling", 827 | "proc-macro2", 828 | "quote", 829 | "syn", 830 | ] 831 | 832 | [[package]] 833 | name = "serde_yaml" 834 | version = "0.9.34+deprecated" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 837 | dependencies = [ 838 | "indexmap", 839 | "itoa", 840 | "ryu", 841 | "serde", 842 | "unsafe-libyaml", 843 | ] 844 | 845 | [[package]] 846 | name = "shell-words" 847 | version = "1.1.0" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 850 | 851 | [[package]] 852 | name = "shlex" 853 | version = "1.3.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 856 | 857 | [[package]] 858 | name = "std_prelude" 859 | version = "0.2.12" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" 862 | 863 | [[package]] 864 | name = "strsim" 865 | version = "0.11.1" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 868 | 869 | [[package]] 870 | name = "syn" 871 | version = "2.0.101" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 874 | dependencies = [ 875 | "proc-macro2", 876 | "quote", 877 | "unicode-ident", 878 | ] 879 | 880 | [[package]] 881 | name = "syn-select" 882 | version = "0.3.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "ea24402791e2625a28bcaf662046e09a48a7610f806688cf35901d78ba938bb4" 885 | dependencies = [ 886 | "syn", 887 | ] 888 | 889 | [[package]] 890 | name = "syntect" 891 | version = "5.2.0" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" 894 | dependencies = [ 895 | "bincode", 896 | "bitflags 1.3.2", 897 | "fancy-regex", 898 | "flate2", 899 | "fnv", 900 | "once_cell", 901 | "regex-syntax", 902 | "serde", 903 | "serde_derive", 904 | "serde_json", 905 | "thiserror", 906 | "walkdir", 907 | ] 908 | 909 | [[package]] 910 | name = "tempfile" 911 | version = "3.20.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 914 | dependencies = [ 915 | "fastrand", 916 | "getrandom", 917 | "once_cell", 918 | "rustix", 919 | "windows-sys 0.59.0", 920 | ] 921 | 922 | [[package]] 923 | name = "termcolor" 924 | version = "1.4.1" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 927 | dependencies = [ 928 | "winapi-util", 929 | ] 930 | 931 | [[package]] 932 | name = "terminal-colorsaurus" 933 | version = "0.4.8" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "b7afe4c174a3cbfb52ebcb11b28965daf74fe9111d4e07e40689d05af06e26e8" 936 | dependencies = [ 937 | "cfg-if", 938 | "libc", 939 | "memchr", 940 | "mio", 941 | "terminal-trx", 942 | "windows-sys 0.59.0", 943 | "xterm-color", 944 | ] 945 | 946 | [[package]] 947 | name = "terminal-trx" 948 | version = "0.2.4" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "975b4233aefa1b02456d5e53b22c61653c743e308c51cf4181191d8ce41753ab" 951 | dependencies = [ 952 | "cfg-if", 953 | "libc", 954 | "windows-sys 0.59.0", 955 | ] 956 | 957 | [[package]] 958 | name = "thiserror" 959 | version = "1.0.69" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 962 | dependencies = [ 963 | "thiserror-impl", 964 | ] 965 | 966 | [[package]] 967 | name = "thiserror-impl" 968 | version = "1.0.69" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 971 | dependencies = [ 972 | "proc-macro2", 973 | "quote", 974 | "syn", 975 | ] 976 | 977 | [[package]] 978 | name = "time" 979 | version = "0.3.41" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 982 | dependencies = [ 983 | "deranged", 984 | "itoa", 985 | "num-conv", 986 | "powerfmt", 987 | "serde", 988 | "time-core", 989 | "time-macros", 990 | ] 991 | 992 | [[package]] 993 | name = "time-core" 994 | version = "0.1.4" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 997 | 998 | [[package]] 999 | name = "time-macros" 1000 | version = "0.2.22" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1003 | dependencies = [ 1004 | "num-conv", 1005 | "time-core", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "toml" 1010 | version = "0.8.22" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 1013 | dependencies = [ 1014 | "indexmap", 1015 | "serde", 1016 | "serde_spanned", 1017 | "toml_datetime", 1018 | "toml_edit", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "toml_datetime" 1023 | version = "0.6.9" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 1026 | dependencies = [ 1027 | "serde", 1028 | ] 1029 | 1030 | [[package]] 1031 | name = "toml_edit" 1032 | version = "0.22.26" 1033 | source = "registry+https://github.com/rust-lang/crates.io-index" 1034 | checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 1035 | dependencies = [ 1036 | "indexmap", 1037 | "serde", 1038 | "serde_spanned", 1039 | "toml_datetime", 1040 | "toml_write", 1041 | "winnow", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "toml_write" 1046 | version = "0.1.1" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 1049 | 1050 | [[package]] 1051 | name = "toolchain_find" 1052 | version = "0.4.0" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" 1055 | dependencies = [ 1056 | "home", 1057 | "once_cell", 1058 | "regex", 1059 | "semver", 1060 | "walkdir", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "unicode-ident" 1065 | version = "1.0.18" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1068 | 1069 | [[package]] 1070 | name = "unicode-width" 1071 | version = "0.1.14" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 1074 | 1075 | [[package]] 1076 | name = "unicode-width" 1077 | version = "0.2.0" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 1080 | 1081 | [[package]] 1082 | name = "unsafe-libyaml" 1083 | version = "0.2.11" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 1086 | 1087 | [[package]] 1088 | name = "utf8parse" 1089 | version = "0.2.2" 1090 | source = "registry+https://github.com/rust-lang/crates.io-index" 1091 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1092 | 1093 | [[package]] 1094 | name = "walkdir" 1095 | version = "2.5.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1098 | dependencies = [ 1099 | "same-file", 1100 | "winapi-util", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "wasi" 1105 | version = "0.11.0+wasi-snapshot-preview1" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1108 | 1109 | [[package]] 1110 | name = "wasi" 1111 | version = "0.14.2+wasi-0.2.4" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1114 | dependencies = [ 1115 | "wit-bindgen-rt", 1116 | ] 1117 | 1118 | [[package]] 1119 | name = "winapi-util" 1120 | version = "0.1.9" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1123 | dependencies = [ 1124 | "windows-sys 0.59.0", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "windows" 1129 | version = "0.56.0" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" 1132 | dependencies = [ 1133 | "windows-core", 1134 | "windows-targets", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "windows-core" 1139 | version = "0.56.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" 1142 | dependencies = [ 1143 | "windows-implement", 1144 | "windows-interface", 1145 | "windows-result", 1146 | "windows-targets", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "windows-implement" 1151 | version = "0.56.0" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" 1154 | dependencies = [ 1155 | "proc-macro2", 1156 | "quote", 1157 | "syn", 1158 | ] 1159 | 1160 | [[package]] 1161 | name = "windows-interface" 1162 | version = "0.56.0" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" 1165 | dependencies = [ 1166 | "proc-macro2", 1167 | "quote", 1168 | "syn", 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "windows-result" 1173 | version = "0.1.2" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" 1176 | dependencies = [ 1177 | "windows-targets", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "windows-sys" 1182 | version = "0.52.0" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1185 | dependencies = [ 1186 | "windows-targets", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "windows-sys" 1191 | version = "0.59.0" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1194 | dependencies = [ 1195 | "windows-targets", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "windows-targets" 1200 | version = "0.52.6" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1203 | dependencies = [ 1204 | "windows_aarch64_gnullvm", 1205 | "windows_aarch64_msvc", 1206 | "windows_i686_gnu", 1207 | "windows_i686_gnullvm", 1208 | "windows_i686_msvc", 1209 | "windows_x86_64_gnu", 1210 | "windows_x86_64_gnullvm", 1211 | "windows_x86_64_msvc", 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "windows_aarch64_gnullvm" 1216 | version = "0.52.6" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1219 | 1220 | [[package]] 1221 | name = "windows_aarch64_msvc" 1222 | version = "0.52.6" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1225 | 1226 | [[package]] 1227 | name = "windows_i686_gnu" 1228 | version = "0.52.6" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1231 | 1232 | [[package]] 1233 | name = "windows_i686_gnullvm" 1234 | version = "0.52.6" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1237 | 1238 | [[package]] 1239 | name = "windows_i686_msvc" 1240 | version = "0.52.6" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1243 | 1244 | [[package]] 1245 | name = "windows_x86_64_gnu" 1246 | version = "0.52.6" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1249 | 1250 | [[package]] 1251 | name = "windows_x86_64_gnullvm" 1252 | version = "0.52.6" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1255 | 1256 | [[package]] 1257 | name = "windows_x86_64_msvc" 1258 | version = "0.52.6" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1261 | 1262 | [[package]] 1263 | name = "winnow" 1264 | version = "0.7.10" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" 1267 | dependencies = [ 1268 | "memchr", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "wit-bindgen-rt" 1273 | version = "0.39.0" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1276 | dependencies = [ 1277 | "bitflags 2.9.1", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "xterm-color" 1282 | version = "1.0.1" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f" 1285 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-expand" 3 | version = "1.0.108" 4 | authors = ["David Tolnay "] 5 | autotests = false 6 | categories = ["development-tools::cargo-plugins", "development-tools::debugging"] 7 | description = "Wrapper around rustc -Zunpretty=expanded. Shows the result of macro expansion and #[derive] expansion." 8 | documentation = "https://github.com/dtolnay/cargo-expand" 9 | edition = "2021" 10 | keywords = ["cargo", "subcommand", "macros"] 11 | license = "MIT OR Apache-2.0" 12 | repository = "https://github.com/dtolnay/cargo-expand" 13 | rust-version = "1.81" 14 | 15 | [features] 16 | default = ["prettyplease"] 17 | prettyplease = [] 18 | 19 | [dependencies] 20 | bat = { version = "0.25", default-features = false, features = ["paging", "regex-fancy"] } 21 | cargo-subcommand-metadata = "0.1" 22 | clap = { version = "4", features = ["deprecated", "derive"] } 23 | clap-cargo = "0.15" 24 | console = "0.15" 25 | fs-err = "3" 26 | home = "0.5" 27 | prettyplease = { version = "0.2.33", features = ["verbatim"] } 28 | proc-macro2 = "1.0.80" 29 | quote = { version = "1.0.35", default-features = false } 30 | semver = "1" 31 | serde = { version = "1.0.183", features = ["derive"] } 32 | shlex = "1.3" 33 | syn = { version = "2.0.85", default-features = false, features = ["clone-impls", "fold", "full", "parsing", "printing", "visit-mut"] } 34 | syn-select = "0.3" 35 | tempfile = "3.0" 36 | termcolor = "1.0" 37 | toml = "0.8" 38 | toolchain_find = "0.4" 39 | 40 | [target.'cfg(all(windows, not(target_vendor = "uwp")))'.dependencies] 41 | windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Com", "Win32_UI_Shell"] } 42 | 43 | [package.metadata.docs.rs] 44 | targets = ["x86_64-unknown-linux-gnu"] 45 | 46 | [workspace] 47 | members = ["tests"] 48 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cargo-expand 2 | 3 | [github](https://github.com/dtolnay/cargo-expand) 4 | [crates.io](https://crates.io/crates/cargo-expand) 5 | [build status](https://github.com/dtolnay/cargo-expand/actions?query=branch%3Amaster) 6 | 7 | Once installed, the following command prints out the result of macro expansion 8 | and `#[derive]` expansion applied to the current crate. 9 | 10 | ```console 11 | $ cargo expand 12 | ``` 13 | 14 | This is a wrapper around the more verbose compiler command: 15 | 16 | ```console 17 | $ cargo rustc --profile=check -- -Zunpretty=expanded 18 | ``` 19 | 20 | ## Installation 21 | 22 | Install with **`cargo install cargo-expand`**. 23 | 24 | This command optionally uses [rustfmt] to format the expanded output. The 25 | resulting code is typically much more readable than what you get from the 26 | compiler. If rustfmt is not available, the expanded code is not formatted. 27 | Install rustfmt with **`rustup component add rustfmt`**. 28 | 29 | Cargo expand relies on unstable compiler flags so it requires a nightly 30 | toolchain to be installed, though does not require nightly to be the default 31 | toolchain or the one with which cargo expand itself is executed. If the default 32 | toolchain is one other than nightly, running `cargo expand` will find and use 33 | nightly anyway. 34 | 35 | [rustfmt]: https://github.com/rust-lang/rustfmt 36 | 37 | ## Example 38 | 39 | #### `$ cat src/main.rs` 40 | 41 | ```rust 42 | #[derive(Debug)] 43 | struct S; 44 | 45 | fn main() { 46 | println!("{:?}", S); 47 | } 48 | ``` 49 | 50 | #### `$ cargo expand` 51 | 52 | ```rust 53 | #![feature(prelude_import)] 54 | #[prelude_import] 55 | use std::prelude::v1::*; 56 | #[macro_use] 57 | extern crate std; 58 | struct S; 59 | #[automatically_derived] 60 | #[allow(unused_qualifications)] 61 | impl ::core::fmt::Debug for S { 62 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 63 | match *self { 64 | S => { 65 | let mut debug_trait_builder = f.debug_tuple("S"); 66 | debug_trait_builder.finish() 67 | } 68 | } 69 | } 70 | } 71 | fn main() { 72 | { 73 | ::std::io::_print(::core::fmt::Arguments::new_v1( 74 | &["", "\n"], 75 | &match (&S,) { 76 | (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)], 77 | }, 78 | )); 79 | }; 80 | } 81 | ``` 82 | 83 | ## Options 84 | 85 | *See `cargo expand --help` for a complete list of options, most of which are 86 | consistent with other Cargo subcommands. Here are a few that are common in the 87 | context of cargo expand.* 88 | 89 | To expand a particular test target: 90 | 91 | `$ cargo expand --test test_something` 92 | 93 | To expand without rustfmt: 94 | 95 | `$ cargo expand --ugly` 96 | 97 | To expand a specific module or type or function only: 98 | 99 | `$ cargo expand path::to::module` 100 | 101 | [![cargo expand punctuated::printing][punctuated.png]][syn] 102 | [![cargo expand token::FatArrow][fatarrow.png]][syn] 103 | 104 | [punctuated.png]: https://raw.githubusercontent.com/dtolnay/cargo-expand/screenshots/punctuated.png 105 | [fatarrow.png]: https://raw.githubusercontent.com/dtolnay/cargo-expand/screenshots/fatarrow.png 106 | [syn]: https://github.com/dtolnay/syn 107 | 108 | ## Configuration 109 | 110 | The cargo expand command reads the `[expand]` section of $CARGO_HOME/config.toml 111 | if there is one (usually ~/.cargo/config.toml). 112 | 113 | Set the default syntax highlighting theme with the `theme` setting: 114 | 115 | ```toml 116 | [expand] 117 | theme = "TwoDark" 118 | ``` 119 | 120 | Run `cargo expand --themes` or `bat --list-themes` to print a list of available 121 | themes. Use `theme = "none"` to disable coloring. 122 | 123 | Change the default coloring disposition (normally `auto`) with the `color` 124 | setting: 125 | 126 | ```toml 127 | [expand] 128 | color = "always" 129 | ``` 130 | 131 | Enable paging of the output with the `pager` setting: 132 | 133 | ```toml 134 | [expand] 135 | pager = true 136 | ``` 137 | 138 | ## Disclaimer 139 | 140 | Be aware that macro expansion to text is a lossy process. This is a debugging 141 | aid only. There should be no expectation that the expanded code can be compiled 142 | successfully, nor that if it compiles then it behaves the same as the original 143 | code. 144 | 145 | For instance the following function returns `3` when compiled ordinarily by Rust 146 | but the expanded code compiles and returns `4`. 147 | 148 | ```rust 149 | fn f() -> i32 { 150 | let x = 1; 151 | 152 | macro_rules! first_x { 153 | () => { x } 154 | } 155 | 156 | let x = 2; 157 | 158 | x + first_x!() 159 | } 160 | ``` 161 | 162 | Refer to [The Book] for more on the considerations around macro hygiene. 163 | 164 | [The Book]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#hygiene 165 | 166 |
167 | 168 | #### License 169 | 170 | 171 | Licensed under either of Apache License, Version 172 | 2.0 or MIT license at your option. 173 | 174 | 175 |
176 | 177 | 178 | Unless you explicitly state otherwise, any contribution intentionally submitted 179 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 180 | be dual licensed as above, without any additional terms or conditions. 181 | 182 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::PathBuf; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rustc-check-cfg=cfg(host_os, values(\"windows\"))"); 8 | 9 | let prettyplease_version = match env::var("DEP_PRETTYPLEASE02_VERSION") { 10 | Ok(prettyplease_version) => format!(r#"Some("{}")"#, prettyplease_version.escape_debug()), 11 | Err(_) => "None".to_owned(), 12 | }; 13 | 14 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 15 | let prettyplease_version_file = out_dir.join("prettyplease.version"); 16 | fs::write(prettyplease_version_file, prettyplease_version).unwrap(); 17 | 18 | let host = env::var_os("HOST").unwrap(); 19 | if let Some("windows") = host.to_str().unwrap().split('-').nth(2) { 20 | println!("cargo:rustc-cfg=host_os=\"windows\""); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/assets.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Result}; 2 | use std::env; 3 | use std::path::PathBuf; 4 | use std::str; 5 | 6 | pub const BAT_VERSION: &str = { 7 | let mut bytes = include_str!("bat.version").as_bytes(); 8 | while let [rest @ .., b'\n' | b'\r'] = bytes { 9 | bytes = rest; 10 | } 11 | if let Ok(version) = str::from_utf8(bytes) { 12 | version 13 | } else { 14 | panic!() 15 | } 16 | }; 17 | 18 | pub fn cache_dir() -> Result { 19 | if let Some(cache_dir) = env::var_os("BAT_CACHE_PATH") { 20 | return Ok(PathBuf::from(cache_dir)); 21 | } 22 | 23 | let home_dir = home::home_dir().ok_or(Error::HomeDir)?; 24 | 25 | #[cfg(windows)] 26 | let cache_dir = windows::cache_dir(&home_dir); 27 | #[cfg(not(windows))] 28 | let cache_dir = xdg::cache_dir(&home_dir); 29 | 30 | Ok(cache_dir.join("bat")) 31 | } 32 | 33 | // Based on etcetera v0.9.0 34 | #[cfg(windows)] 35 | mod windows { 36 | use std::env; 37 | use std::path::{Path, PathBuf}; 38 | 39 | pub fn cache_dir(home_dir: &Path) -> PathBuf { 40 | env::var_os("LOCALAPPDATA") 41 | .filter(|s| !s.is_empty()) 42 | .map(PathBuf::from) 43 | .or_else(dir_crt) 44 | .unwrap_or_else(|| home_dir.join("AppData").join("Local")) 45 | } 46 | 47 | // Ref: https://github.com/rust-lang/cargo/blob/home-0.5.11/crates/home/src/windows.rs 48 | // We should keep this code in sync with the above. 49 | #[cfg(not(target_vendor = "uwp"))] 50 | fn dir_crt() -> Option { 51 | use std::ffi::OsString; 52 | use std::os::windows::ffi::OsStringExt; 53 | use std::ptr; 54 | use std::slice; 55 | use windows_sys::Win32::Foundation::S_OK; 56 | use windows_sys::Win32::System::Com::CoTaskMemFree; 57 | use windows_sys::Win32::UI::Shell::{ 58 | FOLDERID_LocalAppData, SHGetKnownFolderPath, KF_FLAG_DONT_VERIFY, 59 | }; 60 | 61 | extern "C" { 62 | fn wcslen(buf: *const u16) -> usize; 63 | } 64 | 65 | let mut path = ptr::null_mut(); 66 | let S_OK = (unsafe { 67 | SHGetKnownFolderPath( 68 | &FOLDERID_LocalAppData, 69 | KF_FLAG_DONT_VERIFY as u32, 70 | ptr::null_mut(), 71 | &mut path, 72 | ) 73 | }) else { 74 | // Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`. 75 | unsafe { CoTaskMemFree(path.cast()) }; 76 | return None; 77 | }; 78 | 79 | let path_slice = unsafe { slice::from_raw_parts(path, wcslen(path)) }; 80 | let s = OsString::from_wide(path_slice); 81 | unsafe { CoTaskMemFree(path.cast()) }; 82 | Some(PathBuf::from(s)) 83 | } 84 | 85 | #[cfg(target_vendor = "uwp")] 86 | fn dir_crt() -> Option { 87 | None 88 | } 89 | } 90 | 91 | // Based on etcetera v0.9.0 92 | #[cfg(not(windows))] 93 | mod xdg { 94 | use std::env; 95 | use std::path::{Path, PathBuf}; 96 | 97 | pub fn cache_dir(home_dir: &Path) -> PathBuf { 98 | env::var_os("XDG_CACHE_HOME") 99 | .map(PathBuf::from) 100 | .filter(|path| path.is_absolute()) 101 | .unwrap_or_else(|| home_dir.join(".cache/")) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/bat.version: -------------------------------------------------------------------------------- 1 | 0.25.0 2 | -------------------------------------------------------------------------------- /src/cmd.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::process::Command; 3 | 4 | pub trait CommandExt { 5 | fn flag_value(&mut self, k: K, v: V) -> &mut Self 6 | where 7 | K: AsRef, 8 | V: AsRef; 9 | } 10 | 11 | impl CommandExt for Command { 12 | fn flag_value(&mut self, k: K, v: V) -> &mut Self 13 | where 14 | K: AsRef, 15 | V: AsRef, 16 | { 17 | let k = k.as_ref(); 18 | let v = v.as_ref(); 19 | if let Some(k) = k.to_str() { 20 | if let Some(v) = v.to_str() { 21 | self.arg(format!("{}={}", k, v)); 22 | return self; 23 | } 24 | } 25 | self.arg(k); 26 | self.arg(v); 27 | self 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use std::env; 3 | use std::io::{self, Write}; 4 | use std::path::PathBuf; 5 | 6 | #[derive(Deserialize)] 7 | struct Sections { 8 | #[serde(default)] 9 | expand: Config, 10 | } 11 | 12 | #[derive(Deserialize, Default)] 13 | pub struct Config { 14 | pub theme: Option, 15 | pub color: Option, 16 | #[serde(default)] 17 | pub pager: bool, 18 | /// Format using rustfmt instead of prettyplease. This is significantly 19 | /// slower, and less reliable on macro-generated code, but produces more 20 | /// aesthetic formatting when it works. 21 | #[serde(default)] 22 | pub rustfmt: bool, 23 | } 24 | 25 | pub fn deserialize() -> Config { 26 | try_deserialize().unwrap_or_default() 27 | } 28 | 29 | fn try_deserialize() -> Option { 30 | let cargo_home = env::var_os("CARGO_HOME").map(PathBuf::from)?; 31 | let config_names = ["config", "config.toml"]; 32 | let config_path = config_names 33 | .iter() 34 | .map(|name| cargo_home.join(name)) 35 | .find(|path| path.exists())?; 36 | 37 | let content = fs_err::read_to_string(&config_path).ok()?; 38 | 39 | let full_config: Sections = match toml::from_str(&content) { 40 | Ok(config) => config, 41 | Err(err) => { 42 | let _ = writeln!(io::stderr(), "Warning: {}: {}", config_path.display(), err); 43 | return None; 44 | } 45 | }; 46 | 47 | Some(full_config.expand) 48 | } 49 | -------------------------------------------------------------------------------- /src/edit.rs: -------------------------------------------------------------------------------- 1 | use syn::visit_mut::{self, VisitMut}; 2 | use syn::{Block, File, Item, ItemMod, Stmt}; 3 | 4 | pub fn sanitize(syntax_tree: &mut File) { 5 | remove_macro_rules_from_vec_item(&mut syntax_tree.items); 6 | Sanitize.visit_file_mut(syntax_tree); 7 | } 8 | 9 | // - Remove all macro_rules 10 | // - Remove doc attributes on statements (dtolnay/cargo-expand#71) 11 | struct Sanitize; 12 | 13 | impl VisitMut for Sanitize { 14 | fn visit_item_mod_mut(&mut self, i: &mut ItemMod) { 15 | if let Some((_, items)) = &mut i.content { 16 | remove_macro_rules_from_vec_item(items); 17 | } 18 | visit_mut::visit_item_mod_mut(self, i); 19 | } 20 | 21 | fn visit_block_mut(&mut self, i: &mut Block) { 22 | i.stmts.retain(|stmt| match stmt { 23 | Stmt::Item(Item::Macro(_)) => false, 24 | _ => true, 25 | }); 26 | visit_mut::visit_block_mut(self, i); 27 | } 28 | } 29 | 30 | fn remove_macro_rules_from_vec_item(items: &mut Vec) { 31 | items.retain(|item| match item { 32 | Item::Macro(_) => false, 33 | _ => true, 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::fmt::{self, Display}; 3 | use std::io; 4 | 5 | #[derive(Debug)] 6 | pub enum Error { 7 | Io(io::Error), 8 | TomlSer(toml::ser::Error), 9 | TomlDe(toml::de::Error), 10 | Quote(shlex::QuoteError), 11 | HomeDir, 12 | Bat(bat::error::Error), 13 | } 14 | 15 | pub type Result = std::result::Result; 16 | 17 | impl From for Error { 18 | fn from(error: io::Error) -> Self { 19 | Error::Io(error) 20 | } 21 | } 22 | 23 | impl From for Error { 24 | fn from(error: toml::ser::Error) -> Self { 25 | Error::TomlSer(error) 26 | } 27 | } 28 | 29 | impl From for Error { 30 | fn from(error: toml::de::Error) -> Self { 31 | Error::TomlDe(error) 32 | } 33 | } 34 | 35 | impl From for Error { 36 | fn from(error: shlex::QuoteError) -> Self { 37 | Error::Quote(error) 38 | } 39 | } 40 | 41 | impl From for Error { 42 | fn from(error: bat::error::Error) -> Self { 43 | Error::Bat(error) 44 | } 45 | } 46 | 47 | impl Display for Error { 48 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 49 | match self { 50 | Error::Io(e) => e.fmt(formatter), 51 | Error::TomlSer(e) => e.fmt(formatter), 52 | Error::TomlDe(e) => e.fmt(formatter), 53 | Error::Quote(e) => e.fmt(formatter), 54 | Error::HomeDir => formatter.write_str("could not locate home directory"), 55 | Error::Bat(e) => e.fmt(formatter), 56 | } 57 | } 58 | } 59 | 60 | impl StdError for Error { 61 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 62 | match self { 63 | Error::Io(e) => e.source(), 64 | Error::TomlSer(e) => e.source(), 65 | Error::TomlDe(e) => e.source(), 66 | Error::Quote(e) => e.source(), 67 | Error::HomeDir => None, 68 | Error::Bat(e) => e.source(), 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/fmt.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use serde::Serialize; 3 | use std::path::Path; 4 | 5 | #[derive(Serialize)] 6 | struct Rustfmt { 7 | normalize_doc_attributes: bool, 8 | reorder_imports: bool, 9 | reorder_modules: bool, 10 | } 11 | 12 | pub fn write_rustfmt_config(outdir: impl AsRef) -> Result<()> { 13 | let config = Rustfmt { 14 | normalize_doc_attributes: true, 15 | reorder_imports: false, 16 | reorder_modules: false, 17 | }; 18 | 19 | let toml_string = toml::to_string(&config)?; 20 | 21 | let rustfmt_config_path = outdir.as_ref().join("rustfmt.toml"); 22 | fs_err::write(rustfmt_config_path, toml_string)?; 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::doc_markdown, 3 | clippy::items_after_statements, 4 | clippy::manual_assert, 5 | clippy::manual_strip, 6 | clippy::match_like_matches_macro, 7 | clippy::match_same_arms, // https://github.com/rust-lang/rust-clippy/issues/10327 8 | clippy::module_name_repetitions, 9 | clippy::needless_pass_by_value, 10 | clippy::needless_return, 11 | clippy::new_without_default, 12 | clippy::option_option, 13 | clippy::struct_excessive_bools, 14 | clippy::too_many_lines, 15 | clippy::trivially_copy_pass_by_ref, 16 | clippy::uninlined_format_args, 17 | )] 18 | 19 | mod assets; 20 | mod cmd; 21 | mod config; 22 | mod edit; 23 | mod error; 24 | mod fmt; 25 | mod manifest; 26 | mod opts; 27 | mod unparse; 28 | mod version; 29 | 30 | use crate::cmd::CommandExt as _; 31 | use crate::config::Config; 32 | use crate::error::Result; 33 | use crate::opts::{Coloring, Expand, Subcommand}; 34 | use crate::unparse::unparse_maximal; 35 | use crate::version::Version; 36 | use bat::assets::HighlightingAssets; 37 | use bat::assets_metadata::AssetsMetadata; 38 | use bat::config::VisibleLines; 39 | use bat::line_range::{HighlightedLineRanges, LineRanges}; 40 | use bat::style::StyleComponents; 41 | use bat::theme::{ThemeName, ThemeOptions, ThemePreference}; 42 | use bat::{PagingMode, SyntaxMapping, WrappingMode}; 43 | use clap::{CommandFactory as _, Parser, ValueEnum}; 44 | use quote::quote; 45 | use std::env; 46 | use std::error::Error as StdError; 47 | use std::ffi::{OsStr, OsString}; 48 | use std::io::{self, BufRead, IsTerminal, Write}; 49 | use std::iter; 50 | use std::panic::{self, UnwindSafe}; 51 | use std::path::{Path, PathBuf}; 52 | use std::process::{self, Command, Stdio}; 53 | use std::ptr; 54 | use std::str; 55 | use std::thread::Result as ThreadResult; 56 | use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor}; 57 | 58 | #[allow(deprecated)] // https://github.com/dtolnay/cargo-expand/issues/229 59 | use std::panic::PanicInfo; 60 | 61 | cargo_subcommand_metadata::description!("Show result of macro expansion"); 62 | 63 | fn main() { 64 | let result = if let Some(wrapper) = env::var_os(CARGO_EXPAND_RUSTC_WRAPPER) { 65 | do_rustc_wrapper(&wrapper) 66 | } else { 67 | do_cargo_expand() 68 | }; 69 | 70 | process::exit(match result { 71 | Ok(code) => code, 72 | Err(err) => { 73 | let mut stderr = io::stderr().lock(); 74 | let _ = writeln!(stderr, "{}", err); 75 | let mut err = &err as &dyn StdError; 76 | while let Some(source) = err.source() { 77 | let _ = writeln!(stderr, "\nCaused by:\n {}", source); 78 | err = source; 79 | } 80 | 1 81 | } 82 | }); 83 | } 84 | 85 | const CARGO_EXPAND_RUSTC_WRAPPER: &str = "CARGO_EXPAND_RUSTC_WRAPPER"; 86 | const ARG_Z_UNPRETTY_EXPANDED: &str = "-Zunpretty=expanded"; 87 | 88 | fn cargo_binary() -> OsString { 89 | env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")) 90 | } 91 | 92 | fn do_rustc_wrapper(wrapper: &OsStr) -> Result { 93 | let mut rustc_command = env::args_os().skip(1); 94 | let mut cmd = if wrapper != "/" { 95 | Command::new(wrapper) 96 | } else if let Some(rustc) = rustc_command.next() { 97 | Command::new(rustc) 98 | } else { 99 | Subcommand::command().print_help()?; 100 | return Ok(1); 101 | }; 102 | 103 | let mut is_unpretty_expanded = false; 104 | for arg in rustc_command { 105 | is_unpretty_expanded |= arg == ARG_Z_UNPRETTY_EXPANDED; 106 | cmd.arg(arg); 107 | } 108 | 109 | if is_unpretty_expanded { 110 | cmd.env("RUSTC_BOOTSTRAP", "1"); 111 | } 112 | 113 | #[cfg(unix)] 114 | { 115 | use crate::error::Error; 116 | use std::os::unix::process::CommandExt as _; 117 | 118 | let err = cmd.exec(); 119 | return Err(Error::Io(err)); 120 | } 121 | 122 | #[cfg(not(unix))] 123 | { 124 | let exit_status = cmd.status()?; 125 | let code = exit_status.code().unwrap_or(1); 126 | return Ok(code); 127 | } 128 | } 129 | 130 | fn do_cargo_expand() -> Result { 131 | let Subcommand::Expand(args) = Subcommand::parse(); 132 | 133 | if args.version { 134 | let version = Version { 135 | verbose: args.verbose, 136 | }; 137 | let _ = writeln!(io::stdout(), "{}", version); 138 | return Ok(0); 139 | } 140 | 141 | let config = config::deserialize(); 142 | 143 | if args.themes { 144 | print_themes()?; 145 | return Ok(0); 146 | } 147 | 148 | if let Some(item) = &args.item { 149 | if args.ugly { 150 | let _ = writeln!( 151 | io::stderr(), 152 | "ERROR: cannot expand single item ({}) in ugly mode.", 153 | item, 154 | ); 155 | return Ok(1); 156 | } 157 | } 158 | 159 | let mut rustfmt = None; 160 | if config.rustfmt { 161 | rustfmt = which_rustfmt(); 162 | if rustfmt.is_none() { 163 | let _ = io::stderr().write_all( 164 | b"ERROR: cargo-expand configuration sets rustfmt=true, but \ 165 | rustfmt is not found. Install rustfmt by running `rustup \ 166 | component add rustfmt --toolchain nightly`.\n", 167 | ); 168 | return Ok(1); 169 | } 170 | } 171 | 172 | let mut builder = tempfile::Builder::new(); 173 | builder.prefix("cargo-expand"); 174 | let outdir = builder.tempdir().expect("failed to create tmp file"); 175 | let outfile_path = outdir.path().join("expanded"); 176 | let color = get_color(&args, &config); 177 | 178 | // Run cargo 179 | let mut cmd = Command::new(cargo_binary()); 180 | apply_args(&mut cmd, &args, color, &outfile_path); 181 | if args.verbose { 182 | print_command(&cmd, color)?; 183 | } 184 | 185 | if needs_rustc_bootstrap() { 186 | if let Ok(current_exe) = env::current_exe() { 187 | let original_wrapper = 188 | env::var_os("RUSTC_WRAPPER").filter(|wrapper| !wrapper.is_empty()); 189 | let wrapper = original_wrapper.as_deref().unwrap_or(OsStr::new("/")); 190 | cmd.env(CARGO_EXPAND_RUSTC_WRAPPER, wrapper); 191 | cmd.env("RUSTC_WRAPPER", current_exe); 192 | } else { 193 | cmd.env("RUSTC_BOOTSTRAP", "1"); 194 | } 195 | } 196 | 197 | let code = filter_err(&mut cmd)?; 198 | 199 | if !outfile_path.exists() { 200 | return Ok(1); 201 | } 202 | 203 | let mut content = fs_err::read_to_string(&outfile_path)?; 204 | if content.is_empty() { 205 | let _ = writeln!(io::stderr(), "ERROR: rustc produced no expanded output"); 206 | return Ok(if code == 0 { 1 } else { code }); 207 | } 208 | 209 | // Format the expanded code 210 | if !args.ugly { 211 | let questionably_formatted = content; 212 | 213 | // Work around rustfmt not being able to parse paths containing $crate. 214 | // This placeholder should be the same width as $crate to preserve 215 | // alignments. 216 | const DOLLAR_CRATE_PLACEHOLDER: &str = "Ξcrate"; 217 | let wip = questionably_formatted.replace("$crate", DOLLAR_CRATE_PLACEHOLDER); 218 | 219 | enum Stage { 220 | Formatted(String), 221 | Unformatted(String), 222 | QuestionablyFormatted, 223 | } 224 | 225 | let mut stage = Stage::QuestionablyFormatted; 226 | 227 | // Discard comments, which are misplaced by the compiler 228 | if let Ok(mut syntax_tree) = syn::parse_file(&wip) { 229 | edit::sanitize(&mut syntax_tree); 230 | if let Some(filter) = args.item { 231 | syntax_tree.shebang = None; 232 | syntax_tree.attrs.clear(); 233 | syntax_tree.items = filter.apply_to(&syntax_tree); 234 | if syntax_tree.items.is_empty() { 235 | let _ = writeln!(io::stderr(), "WARNING: no such item: {}", filter); 236 | return Ok(1); 237 | } 238 | } 239 | if !config.rustfmt { 240 | if let Ok(formatted) = ignore_panic(|| unparse_maximal(&syntax_tree)) { 241 | stage = Stage::Formatted(formatted); 242 | } 243 | } 244 | if let Stage::QuestionablyFormatted = stage { 245 | let unformatted = quote!(#syntax_tree).to_string(); 246 | stage = Stage::Unformatted(unformatted); 247 | } 248 | } 249 | 250 | let to_rustfmt = match &stage { 251 | Stage::Formatted(_) => None, 252 | Stage::Unformatted(unformatted) => Some(unformatted), 253 | Stage::QuestionablyFormatted => Some(&wip), 254 | }; 255 | 256 | if let Some(unformatted) = to_rustfmt { 257 | if let Some(rustfmt) = rustfmt.or_else(which_rustfmt) { 258 | fs_err::write(&outfile_path, unformatted)?; 259 | 260 | fmt::write_rustfmt_config(&outdir)?; 261 | 262 | for edition in &["2021", "2018", "2015"] { 263 | let output = Command::new(&rustfmt) 264 | .flag_value("--edition", edition) 265 | .arg(&outfile_path) 266 | .stderr(Stdio::null()) 267 | .output(); 268 | if let Ok(output) = output { 269 | if output.status.success() { 270 | stage = Stage::Formatted(fs_err::read_to_string(&outfile_path)?); 271 | break; 272 | } 273 | } 274 | } 275 | } 276 | } 277 | 278 | content = match stage { 279 | Stage::Formatted(formatted) => formatted.replace(DOLLAR_CRATE_PLACEHOLDER, "$crate"), 280 | Stage::Unformatted(_) | Stage::QuestionablyFormatted => questionably_formatted, 281 | }; 282 | } 283 | 284 | // Run pretty printer 285 | let mut theme = args.theme.or(config.theme); 286 | let none_theme = theme.as_deref() == Some("none"); 287 | let do_color = match color { 288 | Coloring::Always => true, 289 | Coloring::Never => false, 290 | Coloring::Auto => !none_theme && io::stdout().is_terminal(), 291 | }; 292 | let _ = writeln!(io::stderr()); 293 | if do_color { 294 | let theme_result = bat::theme::theme(ThemeOptions { 295 | theme: theme 296 | .clone() 297 | .or_else(|| env::var(bat::theme::env::BAT_THEME).ok()) 298 | .map_or_else(ThemePreference::default, ThemePreference::new), 299 | theme_dark: env::var(bat::theme::env::BAT_THEME_DARK) 300 | .ok() 301 | .map(ThemeName::new), 302 | theme_light: env::var(bat::theme::env::BAT_THEME_LIGHT) 303 | .ok() 304 | .map(ThemeName::new), 305 | }); 306 | match theme_result.theme { 307 | ThemeName::Named(named) => theme = Some(named), 308 | ThemeName::Default => { 309 | if let Some(color_scheme) = theme_result.color_scheme { 310 | let default_theme = bat::theme::default_theme(color_scheme); 311 | theme = Some(default_theme.to_owned()); 312 | } 313 | } 314 | } 315 | let mut assets = HighlightingAssets::from_binary(); 316 | if let Some(requested_theme) = &theme { 317 | if !assets 318 | .themes() 319 | .any(|supported_theme| supported_theme == requested_theme) 320 | { 321 | let cache_dir = assets::cache_dir()?; 322 | if let Some(metadata) = AssetsMetadata::load_from_folder(&cache_dir)? { 323 | if metadata.is_compatible_with(assets::BAT_VERSION) { 324 | assets = HighlightingAssets::from_cache(&cache_dir)?; 325 | } 326 | } 327 | } 328 | } 329 | let config = bat::config::Config { 330 | language: Some("rust"), 331 | show_nonprintable: false, 332 | term_width: console::Term::stdout().size().1 as usize, 333 | tab_width: 4, 334 | colored_output: true, 335 | true_color: false, 336 | style_components: StyleComponents::new(&[]), 337 | wrapping_mode: WrappingMode::default(), 338 | paging_mode: if config.pager { 339 | PagingMode::QuitIfOneScreen 340 | } else { 341 | PagingMode::Never 342 | }, 343 | visible_lines: VisibleLines::Ranges(LineRanges::all()), 344 | theme: theme.unwrap_or_else(String::new), 345 | syntax_mapping: SyntaxMapping::new(), 346 | pager: None, 347 | use_italic_text: false, 348 | highlighted_lines: HighlightedLineRanges(LineRanges::none()), 349 | ..Default::default() 350 | }; 351 | let controller = bat::controller::Controller::new(&config, &assets); 352 | let inputs = vec![bat::input::Input::from_reader(Box::new(content.as_bytes()))]; 353 | // Ignore any errors. 354 | let _ = controller.run(inputs, None); 355 | } else { 356 | let _ = write!(io::stdout(), "{}", content); 357 | } 358 | 359 | Ok(0) 360 | } 361 | 362 | fn which_rustfmt() -> Option { 363 | match env::var_os("RUSTFMT") { 364 | Some(which) => { 365 | if which.is_empty() { 366 | None 367 | } else { 368 | Some(PathBuf::from(which)) 369 | } 370 | } 371 | None => toolchain_find::find_installed_component("rustfmt"), 372 | } 373 | } 374 | 375 | fn apply_args(cmd: &mut Command, args: &Expand, color: Coloring, outfile: &Path) { 376 | cmd.arg("rustc"); 377 | 378 | if args.verbose { 379 | cmd.arg("--verbose"); 380 | } 381 | 382 | match color { 383 | Coloring::Auto => { 384 | if cfg!(not(windows)) && io::stderr().is_terminal() { 385 | cmd.flag_value("--color", "always"); 386 | } else { 387 | cmd.flag_value("--color", "never"); 388 | } 389 | } 390 | color => { 391 | cmd.flag_value("--color", color.to_possible_value().unwrap().get_name()); 392 | } 393 | } 394 | 395 | for kv in &args.config { 396 | cmd.flag_value("--config", kv); 397 | } 398 | 399 | for unstable_flag in &args.unstable_flags { 400 | cmd.arg(format!("-Z{}", unstable_flag)); 401 | } 402 | 403 | if let Some(opt_package) = &args.package { 404 | if let Some(package) = opt_package { 405 | cmd.flag_value("--package", package); 406 | } else { 407 | cmd.arg("--package"); 408 | } 409 | } 410 | 411 | let mut has_explicit_build_target = false; 412 | if args.lib { 413 | cmd.arg("--lib"); 414 | has_explicit_build_target = true; 415 | } 416 | 417 | if let Some(opt_bin) = &args.bin { 418 | if let Some(bin) = opt_bin { 419 | cmd.flag_value("--bin", bin); 420 | } else { 421 | cmd.arg("--bin"); 422 | } 423 | has_explicit_build_target = true; 424 | } 425 | 426 | if let Some(opt_example) = &args.example { 427 | if let Some(example) = opt_example { 428 | cmd.flag_value("--example", example); 429 | } else { 430 | cmd.arg("--example"); 431 | } 432 | has_explicit_build_target = true; 433 | } 434 | 435 | if let Some(opt_test) = &args.test { 436 | if let Some(test) = opt_test { 437 | cmd.flag_value("--test", test); 438 | } else { 439 | cmd.arg("--test"); 440 | } 441 | has_explicit_build_target = true; 442 | } 443 | 444 | if let Some(opt_bench) = &args.bench { 445 | if let Some(bench) = opt_bench { 446 | cmd.flag_value("--bench", bench); 447 | } else { 448 | cmd.arg("--bench"); 449 | } 450 | has_explicit_build_target = true; 451 | } 452 | 453 | if !has_explicit_build_target { 454 | if let Ok(cargo_manifest) = manifest::parse(args.manifest_path.as_deref()) { 455 | if let Some(root_package) = cargo_manifest.package { 456 | if let Some(default_run) = &root_package.default_run { 457 | cmd.flag_value("--bin", default_run); 458 | } 459 | } 460 | } 461 | } 462 | 463 | if let Some(features) = &args.features { 464 | cmd.flag_value("--features", features); 465 | } 466 | 467 | if args.all_features { 468 | cmd.arg("--all-features"); 469 | } 470 | 471 | if args.no_default_features { 472 | cmd.arg("--no-default-features"); 473 | } 474 | 475 | if let Some(jobs) = args.jobs { 476 | cmd.flag_value("--jobs", jobs.to_string()); 477 | } 478 | 479 | if let Some(profile) = &args.profile { 480 | cmd.flag_value("--profile", profile); 481 | } else if args.tests && args.test.is_none() { 482 | if args.release { 483 | cmd.flag_value("--profile", "bench"); 484 | } else { 485 | cmd.flag_value("--profile", "test"); 486 | } 487 | } else if args.release { 488 | cmd.flag_value("--profile", "release"); 489 | } else { 490 | cmd.flag_value("--profile", "check"); 491 | } 492 | 493 | if let Some(target) = &args.target { 494 | cmd.flag_value("--target", target); 495 | } 496 | 497 | if let Some(target_dir) = &args.target_dir { 498 | cmd.flag_value("--target-dir", target_dir); 499 | } 500 | 501 | if let Some(manifest_path) = &args.manifest_path { 502 | cmd.flag_value("--manifest-path", manifest_path); 503 | } 504 | 505 | if args.frozen { 506 | cmd.arg("--frozen"); 507 | } 508 | 509 | if args.locked { 510 | cmd.arg("--locked"); 511 | } 512 | 513 | if args.offline { 514 | cmd.arg("--offline"); 515 | } 516 | 517 | cmd.arg("--"); 518 | 519 | cmd.arg("-o"); 520 | cmd.arg(outfile); 521 | cmd.arg(ARG_Z_UNPRETTY_EXPANDED); 522 | } 523 | 524 | fn needs_rustc_bootstrap() -> bool { 525 | if env::var_os("RUSTC_BOOTSTRAP").is_some_and(|var| !var.is_empty()) { 526 | return false; 527 | } 528 | 529 | let rustc = if let Some(rustc) = env::var_os("RUSTC") { 530 | PathBuf::from(rustc) 531 | } else { 532 | let mut cmd = Command::new(cargo_binary()); 533 | cmd.arg("rustc"); 534 | cmd.arg("-Zunstable-options"); 535 | cmd.flag_value("--print", "sysroot"); 536 | cmd.env("RUSTC_BOOTSTRAP", "1"); 537 | cmd.stdin(Stdio::null()); 538 | cmd.stderr(Stdio::null()); 539 | let Ok(output) = cmd.output() else { 540 | return true; 541 | }; 542 | let Ok(stdout) = str::from_utf8(&output.stdout) else { 543 | return true; 544 | }; 545 | let sysroot = Path::new(stdout.trim_end()); 546 | sysroot.join("bin").join("rustc") 547 | }; 548 | 549 | let rustc_wrapper = env::var_os("RUSTC_WRAPPER").filter(|wrapper| !wrapper.is_empty()); 550 | let rustc_workspace_wrapper = 551 | env::var_os("RUSTC_WORKSPACE_WRAPPER").filter(|wrapper| !wrapper.is_empty()); 552 | let mut wrapped_rustc = rustc_wrapper 553 | .into_iter() 554 | .chain(rustc_workspace_wrapper) 555 | .chain(iter::once(rustc.into_os_string())); 556 | 557 | let mut cmd = Command::new(wrapped_rustc.next().unwrap()); 558 | cmd.args(wrapped_rustc); 559 | cmd.arg("-Zunpretty=expanded"); 560 | cmd.arg("-"); 561 | cmd.stdin(Stdio::null()); 562 | cmd.stdout(Stdio::null()); 563 | cmd.stderr(Stdio::null()); 564 | let Ok(status) = cmd.status() else { 565 | return true; 566 | }; 567 | !status.success() 568 | } 569 | 570 | fn print_command(cmd: &Command, color: Coloring) -> Result<()> { 571 | let mut shell_words = String::new(); 572 | let quoter = shlex::Quoter::new().allow_nul(true); 573 | for arg in cmd.get_args() { 574 | let arg_lossy = arg.to_string_lossy(); 575 | shell_words.push(' '); 576 | match arg_lossy.split_once('=') { 577 | Some((flag, value)) if flag.starts_with('-') && flag == quoter.quote(flag)? => { 578 | shell_words.push_str(flag); 579 | shell_words.push('='); 580 | if !value.is_empty() { 581 | shell_words.push_str("er.quote(value)?); 582 | } 583 | } 584 | _ => shell_words.push_str("er.quote(&arg_lossy)?), 585 | } 586 | } 587 | 588 | let color_choice = match color { 589 | Coloring::Auto => ColorChoice::Auto, 590 | Coloring::Always => ColorChoice::Always, 591 | Coloring::Never => ColorChoice::Never, 592 | }; 593 | 594 | let mut stream = StandardStream::stderr(color_choice); 595 | let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green))); 596 | let _ = write!(stream, "{:>12}", "Running"); 597 | let _ = stream.reset(); 598 | let _ = writeln!(stream, " `cargo +nightly{}`", shell_words); 599 | Ok(()) 600 | } 601 | 602 | fn filter_err(cmd: &mut Command) -> io::Result { 603 | let mut child = cmd.stderr(Stdio::piped()).spawn()?; 604 | let mut stderr = io::BufReader::new(child.stderr.take().unwrap()); 605 | let mut line = String::new(); 606 | while let Ok(n) = stderr.read_line(&mut line) { 607 | if n == 0 { 608 | break; 609 | } 610 | if !ignore_cargo_err(&line) { 611 | let _ = write!(io::stderr(), "{}", line); 612 | } 613 | line.clear(); 614 | } 615 | let code = child.wait()?.code().unwrap_or(1); 616 | Ok(code) 617 | } 618 | 619 | fn ignore_cargo_err(line: &str) -> bool { 620 | if line.trim().is_empty() { 621 | return true; 622 | } 623 | 624 | let discarded_lines = [ 625 | "ignoring specified output filename because multiple outputs were \ 626 | requested", 627 | "ignoring specified output filename for 'link' output because multiple \ 628 | outputs were requested", 629 | "ignoring --out-dir flag due to -o flag", 630 | "ignoring -C extra-filename flag due to -o flag", 631 | "due to multiple output types requested, the explicitly specified \ 632 | output file name will be adapted for each output type", 633 | "warning emitted", 634 | "warnings emitted", 635 | ") generated ", 636 | ]; 637 | for s in &discarded_lines { 638 | if line.contains(s) { 639 | return true; 640 | } 641 | } 642 | 643 | false 644 | } 645 | 646 | fn ignore_panic(f: F) -> ThreadResult 647 | where 648 | F: UnwindSafe + FnOnce() -> T, 649 | { 650 | #[allow(deprecated)] // https://github.com/dtolnay/cargo-expand/issues/229 651 | type PanicHook = dyn Fn(&PanicInfo) + Sync + Send + 'static; 652 | 653 | let null_hook: Box = Box::new(|_panic_info| { /* ignore */ }); 654 | let sanity_check = ptr::addr_of!(*null_hook); 655 | let original_hook = panic::take_hook(); 656 | panic::set_hook(null_hook); 657 | 658 | let result = panic::catch_unwind(f); 659 | 660 | let hopefully_null_hook = panic::take_hook(); 661 | panic::set_hook(original_hook); 662 | if sanity_check != &*hopefully_null_hook { 663 | panic!("race condition on std::panic hook"); 664 | } 665 | 666 | result 667 | } 668 | 669 | fn get_color(args: &Expand, config: &Config) -> Coloring { 670 | if let Some(value) = args.color { 671 | return value; 672 | } 673 | 674 | if env::var_os("NO_COLOR").is_some() { 675 | return Coloring::Never; 676 | } 677 | 678 | if let Some(value) = config.color.as_ref() { 679 | let ignore_case = false; 680 | match Coloring::from_str(value.as_str(), ignore_case) { 681 | Ok(color) => return color, 682 | Err(err) => { 683 | let _ = writeln!( 684 | io::stderr(), 685 | "WARNING: invalid color in cargo config: {}", 686 | err 687 | ); 688 | } 689 | } 690 | } 691 | 692 | Coloring::Auto // default 693 | } 694 | 695 | fn print_themes() -> Result<()> { 696 | let mut cache_dir = assets::cache_dir()?; 697 | let metadata = AssetsMetadata::load_from_folder(&cache_dir)?; 698 | let compatible = metadata 699 | .as_ref() 700 | .is_some_and(|m| m.is_compatible_with(assets::BAT_VERSION)); 701 | let assets = if compatible { 702 | HighlightingAssets::from_cache(&cache_dir)? 703 | } else { 704 | HighlightingAssets::from_binary() 705 | }; 706 | 707 | for theme in assets.themes() { 708 | let _ = writeln!(io::stdout(), "{}", theme); 709 | } 710 | 711 | if metadata.is_some() && !compatible { 712 | if let Some(home_dir) = home::home_dir() { 713 | if let Ok(relative) = cache_dir.strip_prefix(home_dir) { 714 | cache_dir = Path::new("~").join(relative); 715 | } 716 | } 717 | let bat_version = semver::Version::parse(assets::BAT_VERSION).unwrap(); 718 | let _ = writeln!( 719 | io::stderr(), 720 | "\nThere may be other themes in {cache_dir} but they are not \ 721 | compatible with the version of bat built into cargo-expand. Run \ 722 | `bat cache --build` with bat v{major}.{minor} to update the cache.", 723 | cache_dir = cache_dir.display(), 724 | major = bat_version.major, 725 | minor = bat_version.minor, 726 | ); 727 | } 728 | 729 | Ok(()) 730 | } 731 | -------------------------------------------------------------------------------- /src/manifest.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use serde::Deserialize; 3 | use std::env; 4 | use std::io::{self, ErrorKind}; 5 | use std::path::{Path, PathBuf}; 6 | 7 | #[derive(Deserialize, Debug)] 8 | pub struct CargoManifest { 9 | pub package: Option, 10 | } 11 | 12 | #[derive(Deserialize, Debug)] 13 | pub struct CargoPackage { 14 | #[serde(rename = "default-run")] 15 | pub default_run: Option, 16 | } 17 | 18 | pub fn parse(manifest_path: Option<&Path>) -> Result { 19 | let manifest_path = find_cargo_manifest(manifest_path)?; 20 | let content = fs_err::read_to_string(manifest_path)?; 21 | let cargo_manifest: CargoManifest = toml::from_str(&content)?; 22 | Ok(cargo_manifest) 23 | } 24 | 25 | fn find_cargo_manifest(manifest_path: Option<&Path>) -> io::Result { 26 | if let Some(manifest_path) = manifest_path { 27 | return Ok(manifest_path.to_owned()); 28 | } 29 | 30 | let dir = env::current_dir()?; 31 | let mut dir = dir.as_path(); 32 | loop { 33 | let path = dir.join("Cargo.toml"); 34 | if path.try_exists()? { 35 | return Ok(path); 36 | } 37 | dir = match dir.parent() { 38 | Some(parent) => parent, 39 | None => return Err(io::Error::new(ErrorKind::NotFound, "Cargo.toml not found")), 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/opts.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, ValueEnum}; 2 | use std::path::PathBuf; 3 | use std::str::FromStr; 4 | use syn_select::Selector; 5 | 6 | // Help headings 7 | const PACKAGE_SELECTION: &str = "Package Selection"; 8 | const TARGET_SELECTION: &str = "Target Selection"; 9 | const FEATURE_SELECTION: &str = "Feature Selection"; 10 | const COMPILATION_OPTIONS: &str = "Compilation Options"; 11 | const MANIFEST_OPTIONS: &str = "Manifest Options"; 12 | 13 | #[derive(Parser)] 14 | #[command( 15 | bin_name = "cargo", 16 | version, 17 | author, 18 | disable_help_subcommand = true, 19 | styles = clap_cargo::style::CLAP_STYLING, 20 | )] 21 | pub enum Subcommand { 22 | /// Show the result of macro expansion. 23 | #[command(name = "expand", version, author, disable_version_flag = true)] 24 | Expand(Expand), 25 | } 26 | 27 | #[derive(Parser, Debug)] 28 | pub struct Expand { 29 | /// Do not attempt to run rustfmt 30 | #[arg(long)] 31 | pub ugly: bool, 32 | 33 | /// Select syntax highlighting theme 34 | #[arg(long, value_name = "NAME")] 35 | pub theme: Option, 36 | 37 | /// Print available syntax highlighting theme names 38 | #[arg(long)] 39 | pub themes: bool, 40 | 41 | /// Print command lines as they are executed 42 | #[arg(long)] 43 | pub verbose: bool, 44 | 45 | /// Syntax highlighting and colored Cargo output (auto, always, never) 46 | #[arg(long, value_name = "WHEN", hide_possible_values = true)] 47 | pub color: Option, 48 | 49 | /// Override a configuration value 50 | #[arg(long, value_name = "KEY=VALUE")] 51 | pub config: Vec, 52 | 53 | /// Unstable (nightly-only) flags to Cargo 54 | #[arg(short = 'Z', value_name = "FLAG")] 55 | pub unstable_flags: Vec, 56 | 57 | /// Print version 58 | #[arg(long)] 59 | pub version: bool, 60 | 61 | /// Package to expand 62 | #[arg(short, long, value_name = "SPEC", num_args = 0..=1, help_heading = PACKAGE_SELECTION)] 63 | pub package: Option>, 64 | 65 | /// Expand only this package's library 66 | #[arg(long, help_heading = TARGET_SELECTION)] 67 | pub lib: bool, 68 | 69 | /// Expand only the specified binary 70 | #[arg(long, value_name = "NAME", num_args = 0..=1, help_heading = TARGET_SELECTION)] 71 | pub bin: Option>, 72 | 73 | /// Expand only the specified example 74 | #[arg(long, value_name = "NAME", num_args = 0..=1, help_heading = TARGET_SELECTION)] 75 | pub example: Option>, 76 | 77 | /// Expand only the specified test target 78 | #[arg(long, value_name = "NAME", num_args = 0..=1, help_heading = TARGET_SELECTION)] 79 | pub test: Option>, 80 | 81 | /// Include tests when expanding the lib or bin 82 | #[arg(long, help_heading = TARGET_SELECTION)] 83 | pub tests: bool, 84 | 85 | /// Expand only the specified bench target 86 | #[arg(long, value_name = "NAME", num_args = 0..=1, help_heading = TARGET_SELECTION)] 87 | pub bench: Option>, 88 | 89 | /// Space or comma separated list of features to activate 90 | #[arg(short = 'F', long, value_name = "FEATURES", help_heading = FEATURE_SELECTION)] 91 | pub features: Option, 92 | 93 | /// Activate all available features 94 | #[arg(long, help_heading = FEATURE_SELECTION)] 95 | pub all_features: bool, 96 | 97 | /// Do not activate the `default` feature 98 | #[arg(long, help_heading = FEATURE_SELECTION)] 99 | pub no_default_features: bool, 100 | 101 | /// Number of parallel jobs, defaults to # of CPUs 102 | #[arg(short, long, value_name = "N", help_heading = COMPILATION_OPTIONS)] 103 | pub jobs: Option, 104 | 105 | /// Build artifacts in release mode, with optimizations 106 | #[arg(long, help_heading = COMPILATION_OPTIONS)] 107 | pub release: bool, 108 | 109 | /// Build artifacts with the specified profile 110 | #[arg(long, value_name = "PROFILE-NAME", help_heading = COMPILATION_OPTIONS)] 111 | pub profile: Option, 112 | 113 | /// Target triple which compiles will be for 114 | #[arg(long, value_name = "TARGET", help_heading = COMPILATION_OPTIONS)] 115 | pub target: Option, 116 | 117 | /// Directory for all generated artifacts 118 | #[arg(long, value_name = "DIRECTORY", help_heading = COMPILATION_OPTIONS)] 119 | pub target_dir: Option, 120 | 121 | /// Path to Cargo.toml 122 | #[arg(long, value_name = "PATH", help_heading = MANIFEST_OPTIONS)] 123 | pub manifest_path: Option, 124 | 125 | /// Require Cargo.lock and cache are up to date 126 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 127 | pub frozen: bool, 128 | 129 | /// Require Cargo.lock is up to date 130 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 131 | pub locked: bool, 132 | 133 | /// Run without accessing the network 134 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 135 | pub offline: bool, 136 | 137 | /// Local path to module or other named item to expand, e.g. os::unix::ffi 138 | #[arg(value_name = "ITEM", value_parser = parse_selector)] 139 | pub item: Option, 140 | } 141 | 142 | #[derive(ValueEnum, Debug, Clone, Copy)] 143 | pub enum Coloring { 144 | Auto, 145 | Always, 146 | Never, 147 | } 148 | 149 | fn parse_selector(s: &str) -> Result::Err> { 150 | if s.starts_with("::") { 151 | s[2..].parse() 152 | } else { 153 | s.parse() 154 | } 155 | } 156 | 157 | #[test] 158 | fn test_cli() { 159 | ::command().debug_assert(); 160 | } 161 | -------------------------------------------------------------------------------- /src/unparse.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span}; 2 | use quote::quote; 3 | use std::panic; 4 | use syn::fold::{self, Fold}; 5 | use syn::punctuated::Punctuated; 6 | use syn::{ 7 | token, Abi, Block, Expr, File, ForeignItem, Generics, ImplItem, Item, ItemConst, ItemFn, 8 | ItemForeignMod, ItemImpl, ItemTrait, ReturnType, Signature, Stmt, Token, TraitItem, Type, 9 | TypeInfer, Visibility, 10 | }; 11 | 12 | pub(crate) fn unparse_maximal(syntax_tree: &File) -> String { 13 | if let Ok(formatted) = panic::catch_unwind(|| prettyplease::unparse(syntax_tree)) { 14 | return formatted; 15 | } 16 | 17 | let redacted = UnparseMaximal.fold_file(syntax_tree.clone()); 18 | prettyplease::unparse(&redacted) 19 | } 20 | 21 | struct UnparseMaximal; 22 | 23 | impl Fold for UnparseMaximal { 24 | fn fold_item(&mut self, item: Item) -> Item { 25 | let mut file = File { 26 | shebang: None, 27 | attrs: Vec::new(), 28 | items: vec![item], 29 | }; 30 | 31 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 32 | let item = file.items.pop().unwrap(); 33 | if ok { 34 | return item; 35 | } 36 | 37 | file.items.push(fold::fold_item(self, item)); 38 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 39 | if ok { 40 | return file.items.pop().unwrap(); 41 | } 42 | 43 | Item::Verbatim(quote!(...)) 44 | } 45 | 46 | fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { 47 | // `fn main() { $stmt }` 48 | let mut file = File { 49 | shebang: None, 50 | attrs: Vec::new(), 51 | items: vec![Item::Fn(ItemFn { 52 | attrs: Vec::new(), 53 | vis: Visibility::Inherited, 54 | sig: Signature { 55 | constness: None, 56 | asyncness: None, 57 | unsafety: None, 58 | abi: None, 59 | fn_token: token::Fn(Span::call_site()), 60 | ident: Ident::new("main", Span::call_site()), 61 | generics: Generics::default(), 62 | paren_token: token::Paren(Span::call_site()), 63 | inputs: Punctuated::new(), 64 | variadic: None, 65 | output: ReturnType::Default, 66 | }, 67 | block: Box::new(Block { 68 | brace_token: token::Brace(Span::call_site()), 69 | stmts: vec![stmt], 70 | }), 71 | })], 72 | }; 73 | 74 | fn unwrap_item_fn(item: &mut Item) -> &mut ItemFn { 75 | match item { 76 | Item::Fn(item) => item, 77 | _ => unreachable!(), 78 | } 79 | } 80 | 81 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 82 | let item_fn = unwrap_item_fn(&mut file.items[0]); 83 | let stmt = item_fn.block.stmts.pop().unwrap(); 84 | if ok { 85 | return stmt; 86 | } 87 | 88 | item_fn.block.stmts.push(fold::fold_stmt(self, stmt)); 89 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 90 | if ok { 91 | let item_fn = unwrap_item_fn(&mut file.items[0]); 92 | return item_fn.block.stmts.pop().unwrap(); 93 | } 94 | 95 | Stmt::Item(Item::Verbatim(quote!(...))) 96 | } 97 | 98 | fn fold_expr(&mut self, expr: Expr) -> Expr { 99 | // `const _: _ = $expr;` 100 | let mut file = File { 101 | shebang: None, 102 | attrs: Vec::new(), 103 | items: vec![Item::Const(ItemConst { 104 | attrs: Vec::new(), 105 | vis: Visibility::Inherited, 106 | const_token: Token![const](Span::call_site()), 107 | ident: Ident::from(Token![_](Span::call_site())), 108 | generics: Generics::default(), 109 | colon_token: Token![:](Span::call_site()), 110 | ty: Box::new(Type::Infer(TypeInfer { 111 | underscore_token: Token![_](Span::call_site()), 112 | })), 113 | eq_token: Token![=](Span::call_site()), 114 | expr: Box::new(expr), 115 | semi_token: Token![;](Span::call_site()), 116 | })], 117 | }; 118 | 119 | fn unwrap_item_const(item: Item) -> ItemConst { 120 | match item { 121 | Item::Const(item) => item, 122 | _ => unreachable!(), 123 | } 124 | } 125 | 126 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 127 | let mut item_const = unwrap_item_const(file.items.pop().unwrap()); 128 | let expr = *item_const.expr; 129 | if ok { 130 | return expr; 131 | } 132 | 133 | item_const.expr = Box::new(fold::fold_expr(self, expr)); 134 | file.items.push(Item::Const(item_const)); 135 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 136 | if ok { 137 | let item_const = unwrap_item_const(file.items.pop().unwrap()); 138 | return *item_const.expr; 139 | } 140 | 141 | Expr::Verbatim(quote!(...)) 142 | } 143 | 144 | fn fold_foreign_item(&mut self, foreign_item: ForeignItem) -> ForeignItem { 145 | // `extern { $foreign_item }` 146 | let mut file = File { 147 | shebang: None, 148 | attrs: Vec::new(), 149 | items: vec![Item::ForeignMod(ItemForeignMod { 150 | attrs: Vec::new(), 151 | unsafety: None, 152 | abi: Abi { 153 | extern_token: Token![extern](Span::call_site()), 154 | name: None, 155 | }, 156 | brace_token: token::Brace(Span::call_site()), 157 | items: vec![foreign_item], 158 | })], 159 | }; 160 | 161 | fn unwrap_item_foreign_mod(item: &mut Item) -> &mut ItemForeignMod { 162 | match item { 163 | Item::ForeignMod(item) => item, 164 | _ => unreachable!(), 165 | } 166 | } 167 | 168 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 169 | let item_foreign_mod = unwrap_item_foreign_mod(&mut file.items[0]); 170 | let foreign_item = item_foreign_mod.items.pop().unwrap(); 171 | if ok { 172 | return foreign_item; 173 | } 174 | 175 | item_foreign_mod 176 | .items 177 | .push(fold::fold_foreign_item(self, foreign_item)); 178 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 179 | if ok { 180 | let item_foreign_mod = unwrap_item_foreign_mod(&mut file.items[0]); 181 | return item_foreign_mod.items.pop().unwrap(); 182 | } 183 | 184 | ForeignItem::Verbatim(quote!(...)) 185 | } 186 | 187 | fn fold_trait_item(&mut self, trait_item: TraitItem) -> TraitItem { 188 | // `trait Trait { $trait_item }` 189 | let mut file = File { 190 | shebang: None, 191 | attrs: Vec::new(), 192 | items: vec![Item::Trait(ItemTrait { 193 | attrs: Vec::new(), 194 | vis: Visibility::Inherited, 195 | unsafety: None, 196 | auto_token: None, 197 | restriction: None, 198 | trait_token: Token![trait](Span::call_site()), 199 | ident: Ident::new("Trait", Span::call_site()), 200 | generics: Generics::default(), 201 | colon_token: None, 202 | supertraits: Punctuated::new(), 203 | brace_token: token::Brace(Span::call_site()), 204 | items: vec![trait_item], 205 | })], 206 | }; 207 | 208 | fn unwrap_item_trait(item: &mut Item) -> &mut ItemTrait { 209 | match item { 210 | Item::Trait(item) => item, 211 | _ => unreachable!(), 212 | } 213 | } 214 | 215 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 216 | let item_trait = unwrap_item_trait(&mut file.items[0]); 217 | let trait_item = item_trait.items.pop().unwrap(); 218 | if ok { 219 | return trait_item; 220 | } 221 | 222 | item_trait 223 | .items 224 | .push(fold::fold_trait_item(self, trait_item)); 225 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 226 | if ok { 227 | let item_trait = unwrap_item_trait(&mut file.items[0]); 228 | return item_trait.items.pop().unwrap(); 229 | } 230 | 231 | TraitItem::Verbatim(quote!(...)) 232 | } 233 | 234 | fn fold_impl_item(&mut self, impl_item: ImplItem) -> ImplItem { 235 | // `impl _ { $impl_item }` 236 | let mut file = File { 237 | shebang: None, 238 | attrs: Vec::new(), 239 | items: vec![Item::Impl(ItemImpl { 240 | attrs: Vec::new(), 241 | defaultness: None, 242 | unsafety: None, 243 | impl_token: Token![impl](Span::call_site()), 244 | generics: Generics::default(), 245 | trait_: None, 246 | self_ty: Box::new(Type::Infer(TypeInfer { 247 | underscore_token: Token![_](Span::call_site()), 248 | })), 249 | brace_token: token::Brace(Span::call_site()), 250 | items: vec![impl_item], 251 | })], 252 | }; 253 | 254 | fn unwrap_item_impl(item: &mut Item) -> &mut ItemImpl { 255 | match item { 256 | Item::Impl(item) => item, 257 | _ => unreachable!(), 258 | } 259 | } 260 | 261 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 262 | let item_impl = unwrap_item_impl(&mut file.items[0]); 263 | let impl_item = item_impl.items.pop().unwrap(); 264 | if ok { 265 | return impl_item; 266 | } 267 | 268 | item_impl.items.push(fold::fold_impl_item(self, impl_item)); 269 | let ok = panic::catch_unwind(|| prettyplease::unparse(&file)).is_ok(); 270 | if ok { 271 | let item_impl = unwrap_item_impl(&mut file.items[0]); 272 | return item_impl.items.pop().unwrap(); 273 | } 274 | 275 | ImplItem::Verbatim(quote!(...)) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | 3 | const CARGO_EXPAND_VERSION: &str = env!("CARGO_PKG_VERSION"); 4 | 5 | #[cfg(not(host_os = "windows"))] 6 | const PRETTYPLEASE_VERSION: Option<&str> = 7 | include!(concat!(env!("OUT_DIR"), "/prettyplease.version")); 8 | 9 | #[cfg(host_os = "windows")] 10 | const PRETTYPLEASE_VERSION: Option<&str> = 11 | include!(concat!(env!("OUT_DIR"), "\\prettyplease.version")); 12 | 13 | pub(crate) struct Version { 14 | pub verbose: bool, 15 | } 16 | 17 | impl Display for Version { 18 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 19 | formatter.write_str("cargo-expand ")?; 20 | formatter.write_str(CARGO_EXPAND_VERSION)?; 21 | if self.verbose { 22 | if let Some(prettyplease_version) = PRETTYPLEASE_VERSION { 23 | formatter.write_str(" + prettyplease ")?; 24 | formatter.write_str(prettyplease_version)?; 25 | } 26 | } 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-expand-test" 3 | version = "0.0.0" 4 | authors = ["David Tolnay "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | path = "lib.rs" 10 | -------------------------------------------------------------------------------- /tests/lib.expand.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | //! Test 3 | #[prelude_import] 4 | use std::prelude::rust_2021::*; 5 | #[macro_use] 6 | extern crate std; 7 | /// Test 8 | pub fn test() {} 9 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | //! Test 2 | 3 | /// Test 4 | pub fn test() {} 5 | --------------------------------------------------------------------------------