├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ └── rust-clippy.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── config.example.yml ├── res └── demo.svg └── src ├── cli.rs ├── config.rs ├── main.rs ├── wg.ini └── wgc.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = true 13 | 14 | 15 | [*.rs] 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | 8 | - package-ecosystem: "cargo" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build release binaries 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | tags: 9 | - "v[0-9]+.[0-9]+.[0-9]*" 10 | 11 | env: 12 | CARGO_INCREMENTAL: 0 13 | CARGO_NET_RETRY: 10 14 | RUSTUP_MAX_RETRIES: 10 15 | 16 | jobs: 17 | github_build: 18 | name: Build release binaries 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | include: 23 | - target: x86_64-unknown-linux-musl 24 | os: ubuntu-latest 25 | name: wgx-x86_64-unknown-linux-musl.tar.gz 26 | 27 | - target: i686-unknown-linux-musl 28 | os: ubuntu-latest 29 | name: wgx-i686-unknown-linux-musl.tar.gz 30 | 31 | - target: aarch64-unknown-linux-musl 32 | os: ubuntu-latest 33 | name: wgx-aarch64-unknown-linux-musl.tar.gz 34 | 35 | - target: arm-unknown-linux-musleabihf 36 | os: ubuntu-latest 37 | name: wgx-arm-unknown-linux-musleabihf.tar.gz 38 | 39 | - target: x86_64-apple-darwin 40 | os: macos-latest 41 | name: wgx-x86_64-apple-darwin.tar.gz 42 | 43 | - target: aarch64-apple-darwin 44 | os: macos-latest 45 | name: wgx-aarch64-apple-darwin.tar.gz 46 | 47 | - target: x86_64-pc-windows-msvc 48 | os: windows-latest 49 | name: wgx-x86_64-pc-windows-msvc.zip 50 | 51 | # issue: https://github.com/briansmith/ring/issues/1461 52 | # - target: aarch64-pc-windows-msvc 53 | # os: windows-latest 54 | # name: wgx-aarch64-pc-windows-msvc.zip 55 | 56 | runs-on: ${{ matrix.os }} 57 | continue-on-error: true 58 | steps: 59 | - name: Setup | Checkout 60 | uses: actions/checkout@v3 61 | 62 | - name: Setup | Rust 63 | uses: actions-rs/toolchain@v1.0.7 64 | with: 65 | toolchain: nightly 66 | override: true 67 | profile: minimal 68 | target: ${{ matrix.target }} 69 | 70 | - name: Build | Build 71 | uses: actions-rs/cargo@v1.0.3 72 | with: 73 | command: build 74 | args: --release --locked --target ${{ matrix.target }} 75 | use-cross: ${{ matrix.os == 'ubuntu-latest' }} 76 | 77 | - name: Post Build | Prepare artifacts [Windows] 78 | if: matrix.os == 'windows-latest' 79 | run: | 80 | cd target/${{ matrix.target }}/release 81 | strip wgx.exe 82 | 7z a ../../../${{ matrix.name }} wgx.exe 83 | cd - 84 | - name: Post Build | Prepare artifacts [-nix] 85 | if: matrix.os != 'windows-latest' 86 | run: | 87 | cd target/${{ matrix.target }}/release 88 | strip wgx || true 89 | tar czvf ../../../${{ matrix.name }} wgx 90 | cd - 91 | - name: Deploy | Upload artifacts 92 | uses: actions/upload-artifact@v4 93 | if: startsWith(github.ref, 'refs/tags/') 94 | with: 95 | name: ${{ matrix.name }} 96 | path: ${{ matrix.name }} 97 | 98 | upload_artifacts: 99 | name: Add Build Artifacts to Release 100 | needs: [github_build] 101 | if: startsWith(github.ref, 'refs/tags/') 102 | runs-on: ubuntu-latest 103 | steps: 104 | - name: Setup | Checkout 105 | uses: actions/checkout@v3 106 | 107 | - name: Setup | Artifacts 108 | uses: actions/download-artifact@v4 109 | 110 | - name: Setup | Checksums 111 | run: for file in wgx-*/wgx-*; do openssl dgst -sha256 -r "$file" | awk '{print $1}' > "${file}.sha256"; done 112 | 113 | - name: Build | Add Artifacts to Release 114 | uses: softprops/action-gh-release@v2 115 | env: 116 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 117 | with: 118 | files: wgx-*/wgx-* 119 | 120 | publish_crates: 121 | name: Publish Crates 122 | if: startsWith(github.ref, 'refs/tags/') 123 | runs-on: ubuntu-latest 124 | steps: 125 | - uses: actions/checkout@v4 126 | - uses: dtolnay/rust-toolchain@nightly 127 | - name: Publish | crates.io 128 | uses: katyo/publish-crates@v2 129 | with: 130 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} 131 | ignore-unpublished-changes: true -------------------------------------------------------------------------------- /.github/workflows/rust-clippy.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # rust-clippy is a tool that runs a bunch of lints to catch common 6 | # mistakes in your Rust code and help improve your Rust code. 7 | # More details at https://github.com/rust-lang/rust-clippy 8 | # and https://rust-lang.github.io/rust-clippy/ 9 | 10 | name: rust-clippy analyze 11 | 12 | on: 13 | push: 14 | branches: [ master ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ master ] 18 | schedule: 19 | - cron: '0 23 * * *' 20 | 21 | jobs: 22 | rust-clippy-analyze: 23 | name: Run rust-clippy analyzing 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v3 31 | 32 | - name: Install Rust toolchain 33 | uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 34 | with: 35 | profile: minimal 36 | toolchain: stable 37 | components: clippy 38 | override: true 39 | 40 | - name: Install required cargo 41 | run: cargo install clippy-sarif sarif-fmt 42 | 43 | - name: Run rust-clippy 44 | run: 45 | cargo clippy 46 | --all-features 47 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 48 | continue-on-error: true 49 | 50 | - name: Upload analysis results to GitHub 51 | uses: github/codeql-action/upload-sarif@v2 52 | with: 53 | sarif_file: rust-clippy-results.sarif 54 | wait-for-processing: true 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /config.yml 3 | !/.github 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "android-tzdata" 16 | version = "0.1.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 19 | 20 | [[package]] 21 | name = "android_system_properties" 22 | version = "0.1.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 | dependencies = [ 26 | "libc", 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", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "3.0.7" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 73 | dependencies = [ 74 | "anstyle", 75 | "once_cell", 76 | "windows-sys", 77 | ] 78 | 79 | [[package]] 80 | name = "anyhow" 81 | version = "1.0.95" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 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 = "bitflags" 99 | version = "2.8.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 102 | 103 | [[package]] 104 | name = "block-buffer" 105 | version = "0.10.4" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 108 | dependencies = [ 109 | "generic-array", 110 | ] 111 | 112 | [[package]] 113 | name = "bstr" 114 | version = "1.11.3" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" 117 | dependencies = [ 118 | "memchr", 119 | "serde", 120 | ] 121 | 122 | [[package]] 123 | name = "bumpalo" 124 | version = "3.17.0" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 127 | 128 | [[package]] 129 | name = "byteorder" 130 | version = "1.5.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 133 | 134 | [[package]] 135 | name = "cc" 136 | version = "1.2.12" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" 139 | dependencies = [ 140 | "shlex", 141 | ] 142 | 143 | [[package]] 144 | name = "cfg-if" 145 | version = "1.0.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 148 | 149 | [[package]] 150 | name = "chrono" 151 | version = "0.4.39" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 154 | dependencies = [ 155 | "android-tzdata", 156 | "iana-time-zone", 157 | "num-traits", 158 | "windows-targets", 159 | ] 160 | 161 | [[package]] 162 | name = "chrono-tz" 163 | version = "0.9.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" 166 | dependencies = [ 167 | "chrono", 168 | "chrono-tz-build", 169 | "phf", 170 | ] 171 | 172 | [[package]] 173 | name = "chrono-tz-build" 174 | version = "0.3.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" 177 | dependencies = [ 178 | "parse-zoneinfo", 179 | "phf", 180 | "phf_codegen", 181 | ] 182 | 183 | [[package]] 184 | name = "clap" 185 | version = "4.5.28" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" 188 | dependencies = [ 189 | "clap_builder", 190 | "clap_derive", 191 | ] 192 | 193 | [[package]] 194 | name = "clap_builder" 195 | version = "4.5.27" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" 198 | dependencies = [ 199 | "anstream", 200 | "anstyle", 201 | "clap_lex", 202 | "strsim", 203 | ] 204 | 205 | [[package]] 206 | name = "clap_derive" 207 | version = "4.5.28" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" 210 | dependencies = [ 211 | "heck", 212 | "proc-macro2", 213 | "quote", 214 | "syn", 215 | ] 216 | 217 | [[package]] 218 | name = "clap_lex" 219 | version = "0.7.4" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 222 | 223 | [[package]] 224 | name = "colorchoice" 225 | version = "1.0.3" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 228 | 229 | [[package]] 230 | name = "console" 231 | version = "0.15.10" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" 234 | dependencies = [ 235 | "encode_unicode", 236 | "libc", 237 | "once_cell", 238 | "unicode-width", 239 | "windows-sys", 240 | ] 241 | 242 | [[package]] 243 | name = "core-foundation-sys" 244 | version = "0.8.7" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 247 | 248 | [[package]] 249 | name = "cpufeatures" 250 | version = "0.2.17" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 253 | dependencies = [ 254 | "libc", 255 | ] 256 | 257 | [[package]] 258 | name = "crossbeam-deque" 259 | version = "0.8.6" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 262 | dependencies = [ 263 | "crossbeam-epoch", 264 | "crossbeam-utils", 265 | ] 266 | 267 | [[package]] 268 | name = "crossbeam-epoch" 269 | version = "0.9.18" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 272 | dependencies = [ 273 | "crossbeam-utils", 274 | ] 275 | 276 | [[package]] 277 | name = "crossbeam-utils" 278 | version = "0.8.21" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 281 | 282 | [[package]] 283 | name = "crossterm" 284 | version = "0.28.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 287 | dependencies = [ 288 | "bitflags", 289 | "crossterm_winapi", 290 | "parking_lot", 291 | "rustix", 292 | "winapi", 293 | ] 294 | 295 | [[package]] 296 | name = "crossterm_winapi" 297 | version = "0.9.1" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 300 | dependencies = [ 301 | "winapi", 302 | ] 303 | 304 | [[package]] 305 | name = "crypto-common" 306 | version = "0.1.6" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 309 | dependencies = [ 310 | "generic-array", 311 | "typenum", 312 | ] 313 | 314 | [[package]] 315 | name = "curve25519-dalek" 316 | version = "4.1.3" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 319 | dependencies = [ 320 | "cfg-if", 321 | "cpufeatures", 322 | "curve25519-dalek-derive", 323 | "fiat-crypto", 324 | "rustc_version", 325 | "subtle", 326 | "zeroize", 327 | ] 328 | 329 | [[package]] 330 | name = "curve25519-dalek-derive" 331 | version = "0.1.1" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 334 | dependencies = [ 335 | "proc-macro2", 336 | "quote", 337 | "syn", 338 | ] 339 | 340 | [[package]] 341 | name = "deunicode" 342 | version = "1.6.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" 345 | 346 | [[package]] 347 | name = "dialoguer" 348 | version = "0.11.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 351 | dependencies = [ 352 | "console", 353 | "fuzzy-matcher", 354 | "shell-words", 355 | "tempfile", 356 | "thiserror 1.0.69", 357 | "zeroize", 358 | ] 359 | 360 | [[package]] 361 | name = "digest" 362 | version = "0.10.7" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 365 | dependencies = [ 366 | "block-buffer", 367 | "crypto-common", 368 | ] 369 | 370 | [[package]] 371 | name = "encode_unicode" 372 | version = "1.0.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 375 | 376 | [[package]] 377 | name = "equivalent" 378 | version = "1.0.1" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 381 | 382 | [[package]] 383 | name = "errno" 384 | version = "0.3.10" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 387 | dependencies = [ 388 | "libc", 389 | "windows-sys", 390 | ] 391 | 392 | [[package]] 393 | name = "fastrand" 394 | version = "2.3.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 397 | 398 | [[package]] 399 | name = "fiat-crypto" 400 | version = "0.2.9" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 403 | 404 | [[package]] 405 | name = "fuzzy-matcher" 406 | version = "0.3.7" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" 409 | dependencies = [ 410 | "thread_local", 411 | ] 412 | 413 | [[package]] 414 | name = "generic-array" 415 | version = "0.14.7" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 418 | dependencies = [ 419 | "typenum", 420 | "version_check", 421 | ] 422 | 423 | [[package]] 424 | name = "getrandom" 425 | version = "0.2.15" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 428 | dependencies = [ 429 | "cfg-if", 430 | "libc", 431 | "wasi 0.11.0+wasi-snapshot-preview1", 432 | ] 433 | 434 | [[package]] 435 | name = "getrandom" 436 | version = "0.3.1" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" 439 | dependencies = [ 440 | "cfg-if", 441 | "libc", 442 | "wasi 0.13.3+wasi-0.2.2", 443 | "windows-targets", 444 | ] 445 | 446 | [[package]] 447 | name = "globset" 448 | version = "0.4.15" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" 451 | dependencies = [ 452 | "aho-corasick", 453 | "bstr", 454 | "log", 455 | "regex-automata", 456 | "regex-syntax", 457 | ] 458 | 459 | [[package]] 460 | name = "globwalk" 461 | version = "0.9.1" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" 464 | dependencies = [ 465 | "bitflags", 466 | "ignore", 467 | "walkdir", 468 | ] 469 | 470 | [[package]] 471 | name = "hashbrown" 472 | version = "0.15.2" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 475 | 476 | [[package]] 477 | name = "heck" 478 | version = "0.5.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 481 | 482 | [[package]] 483 | name = "humansize" 484 | version = "2.1.3" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" 487 | dependencies = [ 488 | "libm", 489 | ] 490 | 491 | [[package]] 492 | name = "iana-time-zone" 493 | version = "0.1.61" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 496 | dependencies = [ 497 | "android_system_properties", 498 | "core-foundation-sys", 499 | "iana-time-zone-haiku", 500 | "js-sys", 501 | "wasm-bindgen", 502 | "windows-core", 503 | ] 504 | 505 | [[package]] 506 | name = "iana-time-zone-haiku" 507 | version = "0.1.2" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 510 | dependencies = [ 511 | "cc", 512 | ] 513 | 514 | [[package]] 515 | name = "ignore" 516 | version = "0.4.23" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" 519 | dependencies = [ 520 | "crossbeam-deque", 521 | "globset", 522 | "log", 523 | "memchr", 524 | "regex-automata", 525 | "same-file", 526 | "walkdir", 527 | "winapi-util", 528 | ] 529 | 530 | [[package]] 531 | name = "indexmap" 532 | version = "2.7.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 535 | dependencies = [ 536 | "equivalent", 537 | "hashbrown", 538 | ] 539 | 540 | [[package]] 541 | name = "ipnet" 542 | version = "2.11.0" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 545 | 546 | [[package]] 547 | name = "is_terminal_polyfill" 548 | version = "1.70.1" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 551 | 552 | [[package]] 553 | name = "itoa" 554 | version = "1.0.14" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 557 | 558 | [[package]] 559 | name = "js-sys" 560 | version = "0.3.77" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 563 | dependencies = [ 564 | "once_cell", 565 | "wasm-bindgen", 566 | ] 567 | 568 | [[package]] 569 | name = "lazy_static" 570 | version = "1.5.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 573 | 574 | [[package]] 575 | name = "libc" 576 | version = "0.2.169" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 579 | 580 | [[package]] 581 | name = "libm" 582 | version = "0.2.11" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" 585 | 586 | [[package]] 587 | name = "linux-raw-sys" 588 | version = "0.4.15" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 591 | 592 | [[package]] 593 | name = "lock_api" 594 | version = "0.4.12" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 597 | dependencies = [ 598 | "autocfg", 599 | "scopeguard", 600 | ] 601 | 602 | [[package]] 603 | name = "log" 604 | version = "0.4.25" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 607 | 608 | [[package]] 609 | name = "memchr" 610 | version = "2.7.4" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 613 | 614 | [[package]] 615 | name = "num-traits" 616 | version = "0.2.19" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 619 | dependencies = [ 620 | "autocfg", 621 | ] 622 | 623 | [[package]] 624 | name = "once_cell" 625 | version = "1.20.3" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 628 | 629 | [[package]] 630 | name = "parking_lot" 631 | version = "0.12.3" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 634 | dependencies = [ 635 | "lock_api", 636 | "parking_lot_core", 637 | ] 638 | 639 | [[package]] 640 | name = "parking_lot_core" 641 | version = "0.9.10" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 644 | dependencies = [ 645 | "cfg-if", 646 | "libc", 647 | "redox_syscall", 648 | "smallvec", 649 | "windows-targets", 650 | ] 651 | 652 | [[package]] 653 | name = "parse-zoneinfo" 654 | version = "0.3.1" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 657 | dependencies = [ 658 | "regex", 659 | ] 660 | 661 | [[package]] 662 | name = "percent-encoding" 663 | version = "2.3.1" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 666 | 667 | [[package]] 668 | name = "pest" 669 | version = "2.7.15" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" 672 | dependencies = [ 673 | "memchr", 674 | "thiserror 2.0.11", 675 | "ucd-trie", 676 | ] 677 | 678 | [[package]] 679 | name = "pest_derive" 680 | version = "2.7.15" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" 683 | dependencies = [ 684 | "pest", 685 | "pest_generator", 686 | ] 687 | 688 | [[package]] 689 | name = "pest_generator" 690 | version = "2.7.15" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" 693 | dependencies = [ 694 | "pest", 695 | "pest_meta", 696 | "proc-macro2", 697 | "quote", 698 | "syn", 699 | ] 700 | 701 | [[package]] 702 | name = "pest_meta" 703 | version = "2.7.15" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" 706 | dependencies = [ 707 | "once_cell", 708 | "pest", 709 | "sha2", 710 | ] 711 | 712 | [[package]] 713 | name = "phf" 714 | version = "0.11.3" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 717 | dependencies = [ 718 | "phf_shared", 719 | ] 720 | 721 | [[package]] 722 | name = "phf_codegen" 723 | version = "0.11.3" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 726 | dependencies = [ 727 | "phf_generator", 728 | "phf_shared", 729 | ] 730 | 731 | [[package]] 732 | name = "phf_generator" 733 | version = "0.11.3" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 736 | dependencies = [ 737 | "phf_shared", 738 | "rand", 739 | ] 740 | 741 | [[package]] 742 | name = "phf_shared" 743 | version = "0.11.3" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 746 | dependencies = [ 747 | "siphasher", 748 | ] 749 | 750 | [[package]] 751 | name = "ppv-lite86" 752 | version = "0.2.20" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 755 | dependencies = [ 756 | "zerocopy", 757 | ] 758 | 759 | [[package]] 760 | name = "proc-macro2" 761 | version = "1.0.93" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 764 | dependencies = [ 765 | "unicode-ident", 766 | ] 767 | 768 | [[package]] 769 | name = "qr2term" 770 | version = "0.3.3" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "6867c60b38e9747a079a19614dbb5981a53f21b9a56c265f3bfdf6011a50a957" 773 | dependencies = [ 774 | "crossterm", 775 | "qrcode", 776 | ] 777 | 778 | [[package]] 779 | name = "qrcode" 780 | version = "0.14.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" 783 | 784 | [[package]] 785 | name = "quote" 786 | version = "1.0.38" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 789 | dependencies = [ 790 | "proc-macro2", 791 | ] 792 | 793 | [[package]] 794 | name = "rand" 795 | version = "0.8.5" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 798 | dependencies = [ 799 | "libc", 800 | "rand_chacha", 801 | "rand_core", 802 | ] 803 | 804 | [[package]] 805 | name = "rand_chacha" 806 | version = "0.3.1" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 809 | dependencies = [ 810 | "ppv-lite86", 811 | "rand_core", 812 | ] 813 | 814 | [[package]] 815 | name = "rand_core" 816 | version = "0.6.4" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 819 | dependencies = [ 820 | "getrandom 0.2.15", 821 | ] 822 | 823 | [[package]] 824 | name = "redox_syscall" 825 | version = "0.5.8" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" 828 | dependencies = [ 829 | "bitflags", 830 | ] 831 | 832 | [[package]] 833 | name = "regex" 834 | version = "1.11.1" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 837 | dependencies = [ 838 | "aho-corasick", 839 | "memchr", 840 | "regex-automata", 841 | "regex-syntax", 842 | ] 843 | 844 | [[package]] 845 | name = "regex-automata" 846 | version = "0.4.9" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 849 | dependencies = [ 850 | "aho-corasick", 851 | "memchr", 852 | "regex-syntax", 853 | ] 854 | 855 | [[package]] 856 | name = "regex-syntax" 857 | version = "0.8.5" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 860 | 861 | [[package]] 862 | name = "rustc_version" 863 | version = "0.4.1" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 866 | dependencies = [ 867 | "semver", 868 | ] 869 | 870 | [[package]] 871 | name = "rustix" 872 | version = "0.38.44" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 875 | dependencies = [ 876 | "bitflags", 877 | "errno", 878 | "libc", 879 | "linux-raw-sys", 880 | "windows-sys", 881 | ] 882 | 883 | [[package]] 884 | name = "rustversion" 885 | version = "1.0.19" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 888 | 889 | [[package]] 890 | name = "ryu" 891 | version = "1.0.19" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 894 | 895 | [[package]] 896 | name = "same-file" 897 | version = "1.0.6" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 900 | dependencies = [ 901 | "winapi-util", 902 | ] 903 | 904 | [[package]] 905 | name = "scopeguard" 906 | version = "1.2.0" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 909 | 910 | [[package]] 911 | name = "semver" 912 | version = "1.0.25" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" 915 | 916 | [[package]] 917 | name = "serde" 918 | version = "1.0.217" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 921 | dependencies = [ 922 | "serde_derive", 923 | ] 924 | 925 | [[package]] 926 | name = "serde_derive" 927 | version = "1.0.217" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 930 | dependencies = [ 931 | "proc-macro2", 932 | "quote", 933 | "syn", 934 | ] 935 | 936 | [[package]] 937 | name = "serde_json" 938 | version = "1.0.138" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" 941 | dependencies = [ 942 | "itoa", 943 | "memchr", 944 | "ryu", 945 | "serde", 946 | ] 947 | 948 | [[package]] 949 | name = "serde_yaml" 950 | version = "0.9.34+deprecated" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 953 | dependencies = [ 954 | "indexmap", 955 | "itoa", 956 | "ryu", 957 | "serde", 958 | "unsafe-libyaml", 959 | ] 960 | 961 | [[package]] 962 | name = "sha2" 963 | version = "0.10.8" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 966 | dependencies = [ 967 | "cfg-if", 968 | "cpufeatures", 969 | "digest", 970 | ] 971 | 972 | [[package]] 973 | name = "shell-words" 974 | version = "1.1.0" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 977 | 978 | [[package]] 979 | name = "shlex" 980 | version = "1.3.0" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 983 | 984 | [[package]] 985 | name = "siphasher" 986 | version = "1.0.1" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 989 | 990 | [[package]] 991 | name = "slug" 992 | version = "0.1.6" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" 995 | dependencies = [ 996 | "deunicode", 997 | "wasm-bindgen", 998 | ] 999 | 1000 | [[package]] 1001 | name = "smallvec" 1002 | version = "1.13.2" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1005 | 1006 | [[package]] 1007 | name = "strsim" 1008 | version = "0.11.1" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1011 | 1012 | [[package]] 1013 | name = "subtle" 1014 | version = "2.6.1" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1017 | 1018 | [[package]] 1019 | name = "syn" 1020 | version = "2.0.98" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 1023 | dependencies = [ 1024 | "proc-macro2", 1025 | "quote", 1026 | "unicode-ident", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "tempfile" 1031 | version = "3.16.0" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" 1034 | dependencies = [ 1035 | "cfg-if", 1036 | "fastrand", 1037 | "getrandom 0.3.1", 1038 | "once_cell", 1039 | "rustix", 1040 | "windows-sys", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "tera" 1045 | version = "1.20.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" 1048 | dependencies = [ 1049 | "chrono", 1050 | "chrono-tz", 1051 | "globwalk", 1052 | "humansize", 1053 | "lazy_static", 1054 | "percent-encoding", 1055 | "pest", 1056 | "pest_derive", 1057 | "rand", 1058 | "regex", 1059 | "serde", 1060 | "serde_json", 1061 | "slug", 1062 | "unic-segment", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "thiserror" 1067 | version = "1.0.69" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1070 | dependencies = [ 1071 | "thiserror-impl 1.0.69", 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "thiserror" 1076 | version = "2.0.11" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" 1079 | dependencies = [ 1080 | "thiserror-impl 2.0.11", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "thiserror-impl" 1085 | version = "1.0.69" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1088 | dependencies = [ 1089 | "proc-macro2", 1090 | "quote", 1091 | "syn", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "thiserror-impl" 1096 | version = "2.0.11" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" 1099 | dependencies = [ 1100 | "proc-macro2", 1101 | "quote", 1102 | "syn", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "thread_local" 1107 | version = "1.1.8" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1110 | dependencies = [ 1111 | "cfg-if", 1112 | "once_cell", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "typenum" 1117 | version = "1.17.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1120 | 1121 | [[package]] 1122 | name = "ucd-trie" 1123 | version = "0.1.7" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 1126 | 1127 | [[package]] 1128 | name = "unic-char-property" 1129 | version = "0.9.0" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 1132 | dependencies = [ 1133 | "unic-char-range", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "unic-char-range" 1138 | version = "0.9.0" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 1141 | 1142 | [[package]] 1143 | name = "unic-common" 1144 | version = "0.9.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 1147 | 1148 | [[package]] 1149 | name = "unic-segment" 1150 | version = "0.9.0" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" 1153 | dependencies = [ 1154 | "unic-ucd-segment", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "unic-ucd-segment" 1159 | version = "0.9.0" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" 1162 | dependencies = [ 1163 | "unic-char-property", 1164 | "unic-char-range", 1165 | "unic-ucd-version", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "unic-ucd-version" 1170 | version = "0.9.0" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 1173 | dependencies = [ 1174 | "unic-common", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "unicode-ident" 1179 | version = "1.0.16" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 1182 | 1183 | [[package]] 1184 | name = "unicode-width" 1185 | version = "0.2.0" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 1188 | 1189 | [[package]] 1190 | name = "unsafe-libyaml" 1191 | version = "0.2.11" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 1194 | 1195 | [[package]] 1196 | name = "utf8parse" 1197 | version = "0.2.2" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1200 | 1201 | [[package]] 1202 | name = "version_check" 1203 | version = "0.9.5" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1206 | 1207 | [[package]] 1208 | name = "walkdir" 1209 | version = "2.5.0" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1212 | dependencies = [ 1213 | "same-file", 1214 | "winapi-util", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "wasi" 1219 | version = "0.11.0+wasi-snapshot-preview1" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1222 | 1223 | [[package]] 1224 | name = "wasi" 1225 | version = "0.13.3+wasi-0.2.2" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 1228 | dependencies = [ 1229 | "wit-bindgen-rt", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "wasm-bindgen" 1234 | version = "0.2.100" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1237 | dependencies = [ 1238 | "cfg-if", 1239 | "once_cell", 1240 | "rustversion", 1241 | "wasm-bindgen-macro", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "wasm-bindgen-backend" 1246 | version = "0.2.100" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1249 | dependencies = [ 1250 | "bumpalo", 1251 | "log", 1252 | "proc-macro2", 1253 | "quote", 1254 | "syn", 1255 | "wasm-bindgen-shared", 1256 | ] 1257 | 1258 | [[package]] 1259 | name = "wasm-bindgen-macro" 1260 | version = "0.2.100" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1263 | dependencies = [ 1264 | "quote", 1265 | "wasm-bindgen-macro-support", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "wasm-bindgen-macro-support" 1270 | version = "0.2.100" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1273 | dependencies = [ 1274 | "proc-macro2", 1275 | "quote", 1276 | "syn", 1277 | "wasm-bindgen-backend", 1278 | "wasm-bindgen-shared", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "wasm-bindgen-shared" 1283 | version = "0.2.100" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1286 | dependencies = [ 1287 | "unicode-ident", 1288 | ] 1289 | 1290 | [[package]] 1291 | name = "wgx" 1292 | version = "0.2.3" 1293 | dependencies = [ 1294 | "anyhow", 1295 | "base64", 1296 | "clap", 1297 | "dialoguer", 1298 | "ipnet", 1299 | "qr2term", 1300 | "rand_core", 1301 | "serde", 1302 | "serde_derive", 1303 | "serde_yaml", 1304 | "tera", 1305 | "x25519-dalek", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "winapi" 1310 | version = "0.3.9" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1313 | dependencies = [ 1314 | "winapi-i686-pc-windows-gnu", 1315 | "winapi-x86_64-pc-windows-gnu", 1316 | ] 1317 | 1318 | [[package]] 1319 | name = "winapi-i686-pc-windows-gnu" 1320 | version = "0.4.0" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1323 | 1324 | [[package]] 1325 | name = "winapi-util" 1326 | version = "0.1.9" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1329 | dependencies = [ 1330 | "windows-sys", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "winapi-x86_64-pc-windows-gnu" 1335 | version = "0.4.0" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1338 | 1339 | [[package]] 1340 | name = "windows-core" 1341 | version = "0.52.0" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1344 | dependencies = [ 1345 | "windows-targets", 1346 | ] 1347 | 1348 | [[package]] 1349 | name = "windows-sys" 1350 | version = "0.59.0" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1353 | dependencies = [ 1354 | "windows-targets", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "windows-targets" 1359 | version = "0.52.6" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1362 | dependencies = [ 1363 | "windows_aarch64_gnullvm", 1364 | "windows_aarch64_msvc", 1365 | "windows_i686_gnu", 1366 | "windows_i686_gnullvm", 1367 | "windows_i686_msvc", 1368 | "windows_x86_64_gnu", 1369 | "windows_x86_64_gnullvm", 1370 | "windows_x86_64_msvc", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "windows_aarch64_gnullvm" 1375 | version = "0.52.6" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1378 | 1379 | [[package]] 1380 | name = "windows_aarch64_msvc" 1381 | version = "0.52.6" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1384 | 1385 | [[package]] 1386 | name = "windows_i686_gnu" 1387 | version = "0.52.6" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1390 | 1391 | [[package]] 1392 | name = "windows_i686_gnullvm" 1393 | version = "0.52.6" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1396 | 1397 | [[package]] 1398 | name = "windows_i686_msvc" 1399 | version = "0.52.6" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1402 | 1403 | [[package]] 1404 | name = "windows_x86_64_gnu" 1405 | version = "0.52.6" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1408 | 1409 | [[package]] 1410 | name = "windows_x86_64_gnullvm" 1411 | version = "0.52.6" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1414 | 1415 | [[package]] 1416 | name = "windows_x86_64_msvc" 1417 | version = "0.52.6" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1420 | 1421 | [[package]] 1422 | name = "wit-bindgen-rt" 1423 | version = "0.33.0" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 1426 | dependencies = [ 1427 | "bitflags", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "x25519-dalek" 1432 | version = "2.0.1" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" 1435 | dependencies = [ 1436 | "curve25519-dalek", 1437 | "rand_core", 1438 | "serde", 1439 | "zeroize", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "zerocopy" 1444 | version = "0.7.35" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1447 | dependencies = [ 1448 | "byteorder", 1449 | "zerocopy-derive", 1450 | ] 1451 | 1452 | [[package]] 1453 | name = "zerocopy-derive" 1454 | version = "0.7.35" 1455 | source = "registry+https://github.com/rust-lang/crates.io-index" 1456 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1457 | dependencies = [ 1458 | "proc-macro2", 1459 | "quote", 1460 | "syn", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "zeroize" 1465 | version = "1.8.1" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1468 | dependencies = [ 1469 | "zeroize_derive", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "zeroize_derive" 1474 | version = "1.4.2" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 1477 | dependencies = [ 1478 | "proc-macro2", 1479 | "quote", 1480 | "syn", 1481 | ] 1482 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgx" 3 | version = "0.2.3" 4 | edition = "2021" 5 | authors = ["yinheli "] 6 | keywords = ["wireguard", "toolkit"] 7 | description = "wireguard tool to manage / generate configuration." 8 | repository = "https://github.com/yinheli/wgx" 9 | license = "MIT" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | clap = { version = "4", features = ["derive"] } 15 | anyhow = "1" 16 | ipnet = "2" 17 | serde = { version = "1", features = ["rc"] } 18 | serde_derive = "1" 19 | serde_yaml = "0.9" 20 | tera = "1" 21 | base64 = "0.22" 22 | x25519-dalek = { version = "2", features = ["static_secrets"] } 23 | rand_core = "0" 24 | dialoguer = { version = "0.11", features = ["fuzzy-select"] } 25 | qr2term = "0.3" 26 | 27 | [profile.release] 28 | opt-level = 'z' 29 | lto = true 30 | strip = true 31 | codegen-units = 1 32 | panic = 'abort' 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2025 yinheli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wgx 2 | 3 | wireguard tool to manage / generate configuration. Maintain one yaml configuration file to quickly build wireguard network. 4 | 5 | ## Usage 6 | 7 | ![wgx usage demo](./res/demo.svg) 8 | _No demo visible here? View it on [asciinema](https://asciinema.org/a/515270)._ 9 | 10 | ```bash 11 | wgx --h 12 | 13 | USAGE: 14 | wgx [OPTIONS] 15 | 16 | OPTIONS: 17 | -a, --all Include all nodes 18 | -c, --config Config file [default: ./config.yml] 19 | -f, --format Output format [default: conf] 20 | -h, --help Print help information 21 | -n, --node Node to export [default: ] 22 | -V, --version Print version information 23 | ``` 24 | 25 | more information please checkout [config.example.yml](./config.example.yml) 26 | 27 | [usage-demo]: https://asciinema.org/a/515270 28 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report a security vulnerability, please email the maintainers at `me@yinheli.com`. Please do not create a Github issue 6 | for security vulnerabilities. 7 | 8 | If you can, please include the following details: 9 | 10 | - An MCVE (minimum complete verifiable example) – this is a short code snippet which demonstrates the error in the 11 | the simplest possible (or just a simple) way. 12 | - Which versions of Yew the vulnerability is present in 13 | - What effects the vulnerability has and how serious the vulnerability is 14 | -------------------------------------------------------------------------------- /config.example.yml: -------------------------------------------------------------------------------- 1 | # range: 10.18.0.1 - 10.18.1.254 2 | network: "10.18.0.0/23" 3 | mtu: 1420 4 | 5 | servers: 6 | - node: "home-gateway" 7 | address: "10.18.0.1" 8 | listenPort: 52000 9 | dns: 1.1.1.1,8.8.8.8 10 | publicAddress: "home.example.com:52000" 11 | privateKey: "oD3F5JxAJwBRW2lA3j43PWtP2/FX/gcpyHOWXAlSU00=" 12 | preUp: 13 | - "echo 1" 14 | - "echo 2" 15 | 16 | routes: 17 | - via: "lab-1" 18 | routes: 19 | - "10.25.0.0/16" 20 | - "192.168.4.0/24,192.168.8.0/24" 21 | - via: "dev-server-1" 22 | routes: 23 | - "10.12.2.0/24" 24 | 25 | - node: "lab-1" 26 | address: "10.18.0.2" 27 | privateKey: "6O0WCSq1uqkD7KCFP8Tn0+HpvfHeiecKNMG8XeD3Emk=" 28 | routes: 29 | - via: "home-gateway" 30 | routes: 31 | - "10.0.1.0/24" 32 | - via: "dev-server-1" 33 | # overwrite endpoint 34 | endpoint: "192.168.3.90:52000" 35 | routes: 36 | - "10.12.2.0/24" 37 | 38 | - node: "dev-server-1" 39 | address: "10.18.0.3" 40 | listenPort: 52000 41 | privateKey: "UJ5WZLAM7wYiF2+U725zmUahhOrXZXRnF66FPpKka2s=" 42 | routes: 43 | - via: "home-gateway" 44 | routes: 45 | - "10.0.1.0/24" 46 | 47 | - node: "android" 48 | address: "10.18.0.4" 49 | privateKey: "sDGWwlIzwBg8NNVSnB4GooMBVFSA7xRCKOqm/hCJumo=" 50 | routes: 51 | - via: "home-gateway" 52 | routes: 53 | - "10.0.1.0/24" 54 | - "10.25.0.0/16" 55 | - "192.168.4.0/24,192.168.8.0/24" 56 | - "10.12.2.0/24" 57 | 58 | - node: "macOS" 59 | address: "10.18.0.5" 60 | privateKey: "wCwOHlbwxIesAEfX6FC2sB6Rwj/Ty0MPyCR0uMefe3U=" 61 | routes: 62 | - via: "home-gateway" 63 | routes: 64 | - "10.0.1.0/24" 65 | - "10.25.0.0/16" 66 | - "192.168.4.0/24,192.168.8.0/24" 67 | - "10.12.2.0/24" 68 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use clap::Parser; 4 | 5 | #[derive(Parser, Debug)] 6 | #[command(version, about)] 7 | pub struct Cli { 8 | /// Config file 9 | #[arg(short, long, value_hint=clap::ValueHint::FilePath, default_value="./config.yml")] 10 | pub config: String, 11 | 12 | /// Node to export 13 | #[arg(short, long, default_value = "")] 14 | pub node: String, 15 | 16 | /// Output format, conf: wg config file, qr: QR code 17 | #[arg(short, long, default_value = "conf")] 18 | pub format: Format, 19 | 20 | /// Include all nodes 21 | #[arg(short, long)] 22 | pub all: bool, 23 | } 24 | 25 | #[derive(Debug, Clone, Copy)] 26 | pub enum Format { 27 | Conf, 28 | Qr, 29 | } 30 | 31 | impl FromStr for Format { 32 | type Err = anyhow::Error; 33 | 34 | fn from_str(s: &str) -> Result { 35 | match s { 36 | "conf" | "c" => Ok(Format::Conf), 37 | "qr" | "q" => Ok(Format::Qr), 38 | _ => Err(anyhow::anyhow!("invalid format: {}", s)), 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::fs; 3 | use std::net::IpAddr; 4 | use std::str::FromStr; 5 | 6 | use base64::{engine::general_purpose, Engine as _}; 7 | use ipnet::IpNet; 8 | use serde_derive::Deserialize; 9 | use serde_derive::Serialize; 10 | use x25519_dalek::PublicKey; 11 | use x25519_dalek::StaticSecret; 12 | 13 | #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 14 | #[serde(rename_all = "camelCase")] 15 | pub struct Config { 16 | pub network: String, 17 | pub mtu: u16, 18 | pub pre_up: Option>, 19 | pub pre_down: Option>, 20 | pub post_up: Option>, 21 | pub post_down: Option>, 22 | pub persistent_keepalive: Option, 23 | pub servers: Vec, 24 | } 25 | 26 | #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 27 | #[serde(rename_all = "camelCase")] 28 | pub struct Server { 29 | pub node: String, 30 | pub address: String, 31 | #[serde(skip_serializing, skip_deserializing)] 32 | pub network: String, 33 | pub listen_port: Option, 34 | pub dns: Option, 35 | pub private_key: String, 36 | #[serde(skip_serializing, skip_deserializing)] 37 | pub public_key: String, 38 | pub public_address: Option, 39 | pub pre_up: Option>, 40 | pub pre_down: Option>, 41 | pub post_up: Option>, 42 | pub post_down: Option>, 43 | pub persistent_keepalive: Option, 44 | pub routes: Option>, 45 | } 46 | 47 | #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 48 | #[serde(rename_all = "camelCase")] 49 | pub struct Route { 50 | pub via: String, 51 | pub routes: Vec, 52 | pub endpoint: Option, 53 | pub persistent_keepalive: Option, 54 | } 55 | 56 | impl Config { 57 | pub fn new(file: &str) -> Result { 58 | let mut config: Self = serde_yaml::from_str(fs::read_to_string(file)?.as_str())?; 59 | 60 | config.valid()?; 61 | 62 | let network = IpNet::from_str(config.network.as_str())?; 63 | 64 | config.servers.iter_mut().for_each(|s| { 65 | s.network = format!("{}/{}", s.address, network.prefix_len()); 66 | 67 | let private_key: [u8; 32] = general_purpose::STANDARD 68 | .decode(&s.private_key) 69 | .unwrap() 70 | .try_into() 71 | .unwrap(); 72 | let private_key = StaticSecret::from(private_key); 73 | let public_key = PublicKey::from(&private_key); 74 | s.public_key = general_purpose::STANDARD.encode(public_key.as_bytes()); 75 | }); 76 | 77 | Ok(config) 78 | } 79 | 80 | fn valid(&self) -> Result<(), anyhow::Error> { 81 | anyhow::ensure!(!self.network.is_empty(), "no network defined"); 82 | anyhow::ensure!(!self.servers.is_empty(), "no servers defined"); 83 | 84 | let network = IpNet::from_str(self.network.as_str())?; 85 | 86 | let addrs = self 87 | .servers 88 | .iter() 89 | .map(|v| IpAddr::from_str(v.address.as_str())) 90 | .collect::, _>>()?; 91 | 92 | anyhow::ensure!( 93 | addrs.len() == self.servers.len(), 94 | "each node should have an unique address" 95 | ); 96 | 97 | for a in addrs.iter() { 98 | anyhow::ensure!( 99 | network.contains(a), 100 | "address {} is not in network {}", 101 | a, 102 | network 103 | ); 104 | } 105 | 106 | let private_keys = self 107 | .servers 108 | .iter() 109 | .map(|v| v.private_key.as_str()) 110 | .collect::>(); 111 | let private_keys: HashSet<&str> = HashSet::from_iter(private_keys.iter().cloned()); 112 | 113 | anyhow::ensure!( 114 | private_keys.len() == self.servers.len(), 115 | "each node should have a private key" 116 | ); 117 | 118 | for v in self.servers.iter() { 119 | if let Some(routes) = &v.routes { 120 | for r in routes.iter() { 121 | let contains = self 122 | .servers 123 | .iter() 124 | .filter(|s| s.node != v.node) 125 | .map(|s| s.node.as_str()) 126 | .any(|x| x == r.via.as_str()); 127 | 128 | anyhow::ensure!(contains, "{} require node {} not found", v.node, r.via); 129 | } 130 | } 131 | } 132 | 133 | Ok(()) 134 | } 135 | } 136 | 137 | #[cfg(test)] 138 | mod tests { 139 | use super::Config; 140 | use crate::wgc::WireguardConfig; 141 | 142 | #[test] 143 | fn it_works() { 144 | let config = Config::new("config.example.yml").unwrap(); 145 | 146 | let config = WireguardConfig::new(&config, "home-gateway", true); 147 | println!("{:?}", config); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | 3 | use clap::Parser; 4 | use config::Config; 5 | use dialoguer::{theme::ColorfulTheme, FuzzySelect}; 6 | use wgc::WireguardConfig; 7 | 8 | mod cli; 9 | mod config; 10 | mod wgc; 11 | 12 | fn main() { 13 | let cli = cli::Cli::parse(); 14 | 15 | let config = match Config::new(&cli.config) { 16 | Ok(c) => c, 17 | Err(e) => { 18 | eprintln!("load config file failed: {}", e); 19 | process::exit(1); 20 | } 21 | }; 22 | 23 | let mut node = cli.node; 24 | 25 | if node.is_empty() { 26 | let nodes = config 27 | .servers 28 | .iter() 29 | .map(|s| s.node.clone()) 30 | .collect::>(); 31 | 32 | let id = FuzzySelect::with_theme(&ColorfulTheme::default()) 33 | .with_prompt("Select node to export:") 34 | .default(0) 35 | .items(&nodes) 36 | .interact(); 37 | 38 | if let Ok(id) = id { 39 | node = nodes[id].clone(); 40 | } else { 41 | println!("No node selected"); 42 | process::exit(0); 43 | } 44 | } 45 | 46 | let cfg = WireguardConfig::new(&config, &node, cli.all); 47 | 48 | match cli.format { 49 | cli::Format::Conf => { 50 | println!("\n\n{}", cfg); 51 | } 52 | cli::Format::Qr => { 53 | let text = cfg.to_string(); 54 | qr2term::print_qr(text).unwrap(); 55 | } 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/wg.ini: -------------------------------------------------------------------------------- 1 | [Interface] 2 | # Name = {{ cfg.interface.node }} 3 | Address = {{ cfg.interface.network }} 4 | ListenPort = {{ cfg.interface.listen_port }} 5 | {% if cfg.interface.dns -%} 6 | DNS = {{ cfg.interface.dns }} 7 | {% endif -%} 8 | PrivateKey = {{ cfg.interface.private_key }} 9 | {% if cfg.interface.mtu -%} 10 | MTU = {{ cfg.interface.mtu }} 11 | {% endif -%} 12 | {% if cfg.interface.pre_up -%} 13 | {% for pre_up in cfg.interface.pre_up -%} 14 | PreUp = {{ pre_up }} 15 | {% endfor -%} 16 | {% endif -%} 17 | {% if cfg.interface.pre_down -%} 18 | {% for pre_down in cfg.interface.pre_down -%} 19 | PreDown = {{ pre_down }} 20 | {% endfor -%} 21 | {% endif -%} 22 | {% if cfg.interface.post_up -%} 23 | {% for post_up in cfg.interface.post_up -%} 24 | PostUp = {{ post_up }} 25 | {% endfor -%} 26 | {% endif -%} 27 | {% if cfg.interface.post_down -%} 28 | {% for post_down in cfg.interface.post_down -%} 29 | PostDown = {{ post_down }} 30 | {% endfor -%} 31 | {% endif -%} 32 | 33 | {% for p in cfg.peers %} 34 | [Peer] 35 | # Name = {{p.node}} 36 | {% for v in p.allowed_ips -%} 37 | AllowedIPs = {{ v }} 38 | {% endfor -%} 39 | {% if p.endpoint -%} 40 | Endpoint = {{ p.endpoint }} 41 | {% endif -%} 42 | PublicKey = {{ p.public_key }} 43 | {% if p.persistent_keepalive -%} 44 | PersistentKeepalive = {{ p.persistent_keepalive }} 45 | {% endif -%} 46 | {% endfor -%} 47 | -------------------------------------------------------------------------------- /src/wgc.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use serde_derive::Serialize; 4 | use tera::{Context, Tera}; 5 | 6 | use crate::config::{self, Config}; 7 | 8 | #[derive(Serialize, Debug)] 9 | pub struct WireguardConfig { 10 | pub interface: Interface, 11 | pub peers: Vec, 12 | } 13 | 14 | impl WireguardConfig { 15 | pub fn new(config: &Config, node: &str, include_all: bool) -> Self { 16 | let server = config.servers.iter().find(|v| v.node == node).unwrap(); 17 | let interface = Interface::new(server, config); 18 | 19 | let mut peers = Vec::new(); 20 | if let Some(routes) = &server.routes { 21 | for route in routes { 22 | let peer = Peer::from_route(route, config); 23 | peers.push(peer); 24 | } 25 | } 26 | 27 | if include_all { 28 | for server in &config.servers { 29 | if server.node == node { 30 | continue; 31 | } 32 | 33 | let exists = peers.iter().any(|v| v.node == server.node); 34 | 35 | if exists { 36 | continue; 37 | } 38 | 39 | let peer = Peer::from_server(server); 40 | peers.push(peer); 41 | } 42 | } 43 | 44 | Self { interface, peers } 45 | } 46 | } 47 | 48 | #[derive(Serialize, Debug)] 49 | pub struct Interface { 50 | pub node: String, 51 | pub network: String, 52 | pub listen_port: u16, 53 | pub dns: Option, 54 | pub private_key: String, 55 | pub mtu: u16, 56 | pub pre_up: Option>, 57 | pub pre_down: Option>, 58 | pub post_up: Option>, 59 | pub post_down: Option>, 60 | } 61 | 62 | impl Interface { 63 | pub fn new(server: &config::Server, cfg: &config::Config) -> Self { 64 | Self { 65 | node: server.node.clone(), 66 | network: server.network.clone(), 67 | listen_port: server.listen_port.unwrap_or(0), 68 | dns: server.dns.clone(), 69 | private_key: server.private_key.clone(), 70 | mtu: cfg.mtu, 71 | pre_up: server.pre_up.clone().or_else(|| cfg.pre_up.clone()), 72 | pre_down: server.pre_down.clone().or_else(|| cfg.pre_down.clone()), 73 | post_up: server.post_up.clone().or_else(|| cfg.post_up.clone()), 74 | post_down: server.post_down.clone().or_else(|| cfg.post_down.clone()), 75 | } 76 | } 77 | } 78 | 79 | #[derive(Serialize, Debug)] 80 | pub struct Peer { 81 | pub node: String, 82 | pub public_key: String, 83 | pub allowed_ips: Vec, 84 | pub endpoint: Option, 85 | pub persistent_keepalive: Option, 86 | } 87 | 88 | impl Peer { 89 | pub fn from_server(server: &config::Server) -> Self { 90 | Self { 91 | node: server.node.clone(), 92 | public_key: server.public_key.clone(), 93 | allowed_ips: vec![server.address.clone()], 94 | endpoint: server.public_address.clone(), 95 | persistent_keepalive: server.persistent_keepalive, 96 | } 97 | } 98 | 99 | pub fn from_route(route: &config::Route, cfg: &config::Config) -> Self { 100 | let server = cfg.servers.iter().find(|s| s.node == route.via).unwrap(); 101 | let node = server.node.clone(); 102 | let public_key = server.public_key.clone(); 103 | 104 | let mut allowed_ips = vec![]; 105 | 106 | allowed_ips.push(server.address.clone()); 107 | let mut routes_ips = route.routes.to_vec(); 108 | allowed_ips.append(&mut routes_ips); 109 | 110 | let endpoint = route 111 | .endpoint 112 | .clone() 113 | .or_else(|| server.public_address.clone()); 114 | let persistent_keepalive = route.persistent_keepalive.or(server.persistent_keepalive); 115 | 116 | Self { 117 | node, 118 | public_key, 119 | allowed_ips, 120 | endpoint, 121 | persistent_keepalive, 122 | } 123 | } 124 | } 125 | 126 | impl Display for WireguardConfig { 127 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 128 | let mut tera = Tera::default(); 129 | let tp = include_str!("wg.ini"); 130 | let mut context = Context::new(); 131 | context.insert("cfg", &self); 132 | write!(f, "{}", tera.render_str(tp, &context).unwrap()) 133 | } 134 | } --------------------------------------------------------------------------------