diff --git a/.angulardoc.json b/.angulardoc.json new file mode 100644 index 00000000..253388ca --- /dev/null +++ b/.angulardoc.json @@ -0,0 +1,4 @@ +{ + "repoId": "8f466ce7-4b75-4048-8b8a-cad5bf173aa0", + "lastSync": 0 +} \ No newline at end of file diff --git a/.earthlyignore b/.earthlyignore index 37c45b4d..06918d0b 100644 --- a/.earthlyignore +++ b/.earthlyignore @@ -12,4 +12,14 @@ desktop/angular/.angular # Assets are ignored here because the symlink wouldn't work in # the buildkit container so we copy the assets directly in Earthfile. -desktop/angular/assets \ No newline at end of file +desktop/angular/assets + + +desktop/tauri/src-tauri/target +.gitignore +AUTHORS +CODE_OF_CONDUCT.md +LICENSE +README.md +TESTING.md +TRADEMARKS \ No newline at end of file diff --git a/.gitignore b/.gitignore index 88f8a650..8268448f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,6 @@ # Compiled binaries -portmaster -portmaster.exe -dnsonly -dnsonly.exe -main -main.exe -integrationtest -integrationtest.exe +*.exe +dist/ # Dist dir dist diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Earthfile b/Earthfile index c0da3562..ad1893d9 100644 --- a/Earthfile +++ b/Earthfile @@ -1,10 +1,23 @@ -VERSION --arg-scope-and-set 0.8 +VERSION --arg-scope-and-set --global-cache 0.8 ARG --global go_version = 1.21 ARG --global distro = alpine3.18 ARG --global node_version = 18 ARG --global outputDir = "./dist" +# The list of rust targets we support. They will be automatically converted +# to GOOS, GOARCH and GOARM when building go binaries. See the +RUST_TO_GO_ARCH_STRING +# helper method at the bottom of the file. +ARG --global architectures = "x86_64-unknown-linux-gnu" \ + "aarch64-unknown-linux-gnu" \ + "armv7-unknown-linux-gnueabihf" \ + "arm-unknown-linux-gnueabi" \ + "x86_64-pc-windows-gnu" + +# Import the earthly rust lib since it already provides some useful +# build-targets and methods to initialize the rust toolchain. +IMPORT github.com/earthly/lib/rust:3.0.2 AS rust + go-deps: FROM golang:${go_version}-${distro} WORKDIR /go-workdir @@ -24,7 +37,6 @@ go-deps: COPY go.sum . RUN go mod download - go-base: FROM +go-deps @@ -42,6 +54,15 @@ go-base: # ./assets COPY assets ./assets +# updates all go dependencies and runs go mod tidy, saving go.mod and go.sum locally. +update-go-deps: + FROM +go-base + + RUN go get -u ./.. + RUN go mod tidy + SAVE ARTIFACT go.mod AS LOCAL go.mod + SAVE ARTIFACT --if-exists go.sum AS LOCAL go.sum + # mod-tidy runs 'go mod tidy', saving go.mod and go.sum locally. mod-tidy: FROM +go-base @@ -61,6 +82,9 @@ build-go: ARG GOARM ARG CMDS=portmaster-start portmaster-core hub notifier + # Get the current version + DO +GET_VERSION + CACHE --sharing shared "$GOCACHE" CACHE --sharing shared "$GOMODCACHE" @@ -73,20 +97,17 @@ build-go: # Build all go binaries from the specified in CMDS FOR bin IN $CMDS - RUN go build -o "/tmp/build/" ./cmds/${bin} + RUN go build -o "/tmp/build/" ./cmds/${bin} END - LET NAME = "" - FOR bin IN $(ls -1 "/tmp/build/") - SET NAME = "${outputDir}/${GOOS}_${GOARCH}/${bin}" - IF [ "${GOARM}" != "" ] - SET NAME = "${outputDir}/${GOOS}_${GOARCH}v${GOARM}/${bin}" - END + DO +GO_ARCH_STRING --goos="${GOOS}" --goarch="${GOARCH}" --goarm="${GOARM}" - SAVE ARTIFACT "/tmp/build/${bin}" AS LOCAL "${NAME}" + SAVE ARTIFACT "/tmp/build/${bin}" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/${bin}" END + SAVE ARTIFACT "/tmp/build/" ./output + # Test one or more go packages. # Run `earthly +test-go` to test all packages # Run `earthly +test-go --PKG="service/firewall"` to only test a specific package. @@ -108,29 +129,19 @@ test-go: END test-go-all-platforms: - # Linux platforms: - BUILD +test-go --GOARCH=amd64 --GOOS=linux - BUILD +test-go --GOARCH=arm64 --GOOS=linux - BUILD +test-go --GOARCH=arm --GOOS=linux --GOARM=5 - BUILD +test-go --GOARCH=arm --GOOS=linux --GOARM=6 - BUILD +test-go --GOARCH=arm --GOOS=linux --GOARM=7 - - # Windows platforms: - BUILD +test-go --GOARCH=amd64 --GOOS=windows - BUILD +test-go --GOARCH=arm64 --GOOS=windows + LOCALLY + FOR arch IN ${architectures} + DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}" + BUILD +test-go --GOARCH="${GOARCH}" --GOOS="${GOOS}" --GOARM="${GOARM}" + END # Builds portmaster-start, portmaster-core, hub and notifier for all supported platforms build-go-release: - # Linux platforms: - BUILD +build-go --GOARCH=amd64 --GOOS=linux - BUILD +build-go --GOARCH=arm64 --GOOS=linux - BUILD +build-go --GOARCH=arm --GOOS=linux --GOARM=5 - BUILD +build-go --GOARCH=arm --GOOS=linux --GOARM=6 - BUILD +build-go --GOARCH=arm --GOOS=linux --GOARM=7 - - # Windows platforms: - BUILD +build-go --GOARCH=amd64 --GOOS=windows - BUILD +build-go --GOARCH=arm64 --GOOS=windows + LOCALLY + FOR arch IN ${architectures} + DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}" + BUILD +build-go --GOARCH="${GOARCH}" --GOOS="${GOOS}" --GOARM="${GOARM}" + END # Builds all binaries from the cmds/ folder for linux/windows AMD64 # Most utility binaries are never needed on other platforms. @@ -187,13 +198,199 @@ angular-project: # Build the angular projects (portmaster-UI and tauri-builtin) in production mode angular-release: BUILD +angular-project --project=portmaster --dist=./dist --configuration=production --baseHref=/ui/modules/portmaster - BUILD +angular-project --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref="/" # Build the angular projects (portmaster-UI and tauri-builtin) in dev mode angular-dev: BUILD +angular-project --project=portmaster --dist=./dist --configuration=development --baseHref=/ui/modules/portmaster - BUILD +angular-project --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=development --baseHref="/" + +# A base target for rust to prepare the build container +rust-base: + FROM rust:1.76-bookworm + + RUN apt-get update -qq + RUN apt-get install --no-install-recommends -qq \ + autoconf \ + autotools-dev \ + libtool-bin \ + clang \ + cmake \ + bsdmainutils \ + g++-mingw-w64-x86-64 \ + gcc-aarch64-linux-gnu \ + gcc-arm-none-eabi \ + gcc-arm-linux-gnueabi \ + gcc-arm-linux-gnueabihf \ + libgtk-3-dev \ + libjavascriptcoregtk-4.1-dev \ + libsoup-3.0-dev \ + libwebkit2gtk-4.1-dev \ + build-essential \ + curl \ + wget \ + file \ + libssl-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev + + # Add some required rustup components + RUN rustup component add clippy + RUN rustup component add rustfmt + + # Install toolchains and targets + FOR arch IN ${architectures} + RUN rustup target add ${arch} + END + + DO rust+INIT --keep_fingerprints=true + + # For now we need tauri-cli 1.5 for bulding + DO rust+CARGO --args="install tauri-cli --version ^1.5.11" + +tauri-src: + FROM +rust-base + + WORKDIR /app/tauri + + # --keep-ts is necessary to ensure that the timestamps of the source files + # are preserved such that Rust's incremental compilation works correctly. + COPY --keep-ts ./desktop/tauri/ . + COPY assets/data ./assets + COPY (+angular-project/dist/tauri-builtin --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref="/") ./../angular/dist/tauri-builtin + +build-tauri: + FROM +tauri-src + + ARG --required target + ARG output="release/[^\./]+" + ARG bundle="none" + + # if we want tauri to create the installer bundles we also need to provide all external binaries + # we need to do some magic here because tauri expects the binaries to include the rust target tripple. + # We already knwo that triple because it's a required argument. From that triple, we use +RUST_TO_GO_ARCH_STRING + # function from below to parse the triple and guess wich GOOS and GOARCH we need. + IF [ "${bundle}" != "none" ] + RUN mkdir /tmp/gobuild + RUN mkdir ./binaries + + DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}" + RUN echo "GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} GO_ARCH_STRING=${GO_ARCH_STRING}" + + COPY (+build-go/output --GOOS="${GOOS}" --CMDS="portmaster-start portmaster-core" --GOARCH="${GOARCH}" --GOARM="${GOARM}") /tmp/gobuild + + LET dest="" + FOR bin IN $(ls /tmp/gobuild) + SET dest="./binaries/${bin}-${target}" + + IF [ -z "${bin##*.exe}" ] + SET dest = "./binaries/${bin%.*}-${target}.exe" + END + + RUN echo "Copying ${bin} to ${dest}" + RUN cp "/tmp/gobuild/${bin}" "${dest}" + END + END + + # The following is exected to work but doesn't. for whatever reason cargo-sweep errors out on the windows-toolchain. + # + # DO rust+CARGO --args="tauri build --bundles none --ci --target=${target}" --output="release/[^/\.]+" + # + # For, now, we just directly mount the rust target cache and call cargo ourself. + + DO rust+SET_CACHE_MOUNTS_ENV + RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo tauri build --bundles "${bundle}" --ci --target="${target}" + DO rust+COPY_OUTPUT --output="${output}" + + RUN ls target + +tauri-release: + LOCALLY + + ARG bundle="none" + + FOR arch IN ${architectures} + BUILD +build-tauri --target="${arch}" --bundle="${bundle}" + END release: BUILD +build-go-release BUILD +angular-release + + +# Takes GOOS, GOARCH and optionally GOARM and creates a string representation for file-names. +# in the form of ${GOOS}_{GOARCH} if GOARM is empty, otherwise ${GOOS}_${GOARCH}v${GOARM}. +# Thats the same format as expected and served by our update server. +# +# The result is available as GO_ARCH_STRING environment variable in the build context. +GO_ARCH_STRING: + FUNCTION + ARG --required goos + ARG --required goarch + ARG goarm + + LET result = "${goos}_${goarch}" + IF [ "${goarm}" != "" ] + SET result = "${goos}_${goarch}v${goarm}" + END + + ENV GO_ARCH_STRING="${result}" + +# Takes a rust target (--rustTarget) and extracts architecture and OS and arm version +# and finally calls GO_ARCH_STRING. +# +# The result is available as GO_ARCH_STRING environment variable in the build context. +# It also exports GOOS, GOARCH and GOARM environment variables. +RUST_TO_GO_ARCH_STRING: + FUNCTION + ARG --required rustTarget + + LET goos="" + IF [ -z "${rustTarget##*linux*}" ] + SET goos="linux" + ELSE + SET goos="windows" + END + + + LET goarch="" + LET goarm="" + + IF [ -z "${rustTarget##*x86_64*}" ] + SET goarch="amd64" + ELSE IF [ -z "${rustTarget##*arm*}" ] + SET goarch="arm" + SET goarm="6" + + IF [ -z "${rustTarget##*v7*}" ] + SET goarm="7" + END + ELSE IF [ -z "${rustTarget##*aarch64*}" ] + SET goarch="arm64" + ELSE + RUN echo "GOARCH not detected"; \ + exit 1; + END + + ENV GOOS="${goos}" + ENV GOARCH="${goarch}" + ENV GOARM="${goarm}" + + DO +GO_ARCH_STRING --goos="${goos}" --goarch="${goarch}" --goarm="${goarm}" + +GET_VERSION: + FUNCTION + LOCALLY + + LET VERSION=$(git tag --points-at) + IF [ -z "${VERSION}"] + SET VERSION=$(git describe --tags --abbrev=0)§dev§build + ELSE IF ! git diff --quite + SET VERSION="${VERSION}§dev§build" + END + + RUN echo "Version is ${VERSION}" + ENV VERSION="${VERSION}" + +test: + LOCALLY + + DO +GET_VERSION \ No newline at end of file diff --git a/desktop/angular/package.json b/desktop/angular/package.json index 4131e18f..21207615 100644 --- a/desktop/angular/package.json +++ b/desktop/angular/package.json @@ -15,7 +15,7 @@ "chrome-extension": "NODE_ENV=production ng build --configuration production portmaster-chrome-extension", "chrome-extension:dev": "ng build --configuration development portmaster-chrome-extension --watch", "build": "npm run build-libs && NODE_ENV=production ng build --configuration production --base-href /ui/modules/portmaster/", - "build-tauri": "npm run build-libs && NODE_ENV=production ng build --configuration production", + "build-tauri": "npm run build-libs && NODE_ENV=production ng build --configuration production tauri-builtin", "serve-tauri-builtin": "ng serve tauri-builtin --port 4100", "serve-app": "ng serve --port 4200 --proxy-config ./proxy.json", "tauri-dev": "npm install && run-s build-libs:dev && run-p serve-app serve-tauri-builtin" diff --git a/desktop/tauri/.gitkeep b/desktop/tauri/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/desktop/tauri/assets b/desktop/tauri/assets new file mode 120000 index 00000000..21dab851 --- /dev/null +++ b/desktop/tauri/assets @@ -0,0 +1 @@ +../../assets/data \ No newline at end of file diff --git a/desktop/tauri/src-tauri/.gitignore b/desktop/tauri/src-tauri/.gitignore new file mode 100644 index 00000000..aba21e24 --- /dev/null +++ b/desktop/tauri/src-tauri/.gitignore @@ -0,0 +1,3 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ diff --git a/desktop/tauri/src-tauri/Cargo.lock b/desktop/tauri/src-tauri/Cargo.lock new file mode 100644 index 00000000..a0bda3a8 --- /dev/null +++ b/desktop/tauri/src-tauri/Cargo.lock @@ -0,0 +1,7286 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom 0.2.11", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "app" +version = "0.1.0" +dependencies = [ + "assert_matches", + "cached", + "ctor", + "dataurl", + "dirs", + "futures-util", + "gdk-pixbuf", + "gdk-pixbuf-sys", + "gio-sys 0.18.1", + "glib 0.18.4", + "glib-sys 0.18.1", + "gtk", + "gtk-sys", + "http 1.0.0", + "lazy_static", + "log", + "notify-rust", + "pretty_env_logger", + "rust-ini", + "serde", + "serde_json", + "sha", + "tauri", + "tauri-build", + "tauri-cli", + "tauri-plugin-cli", + "tauri-plugin-clipboard-manager", + "tauri-plugin-dialog", + "tauri-plugin-notification", + "tauri-plugin-os", + "tauri-plugin-shell", + "tauri-plugin-single-instance", + "thiserror", + "tokio", + "tokio-websockets", + "url", + "uuid", + "which", + "windows 0.54.0", + "windows-service", +] + +[[package]] +name = "ar" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" + +[[package]] +name = "arboard" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08" +dependencies = [ + "clipboard-win", + "core-graphics 0.22.3", + "image", + "log", + "objc", + "objc-foundation", + "objc_id", + "parking_lot", + "thiserror", + "winapi", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.2.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 2.1.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +dependencies = [ + "async-lock 3.2.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.1.0", + "parking", + "polling 3.3.1", + "rustix 0.38.30", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +dependencies = [ + "event-listener 4.0.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.30", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.30", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib 0.18.4", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.21.5", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.11", + "http-body", + "hyper", + "itoa 1.0.10", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.11", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock 3.2.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.1.0", + "piper", + "tracing", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bswap" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3acc5ce9c60e68df21b877f13f908ef95c89f01cb6c656cf76ba95f10bc72f5" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cached" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c8c50262271cdf5abc979a5f76515c234e764fa025d1ba4862c0f0bcda0e95" +dependencies = [ + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "hashbrown 0.14.3", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached_proc_macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + +[[package]] +name = "cairo-rs" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1" +dependencies = [ + "bitflags 2.4.1", + "cairo-sys-rs", + "glib 0.18.4", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + +[[package]] +name = "cargo_toml" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1ece59890e746567b467253aea0adbe8a21784d0b025d8a306f66c391c2957" +dependencies = [ + "serde", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.10.0", +] + +[[package]] +name = "clap_complete" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" +dependencies = [ + "clap 4.4.11", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.23.1", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "common-path" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-random" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.11", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "ctor" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctrlc" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +dependencies = [ + "nix 0.27.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "dataurl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17a1f14ed857323d318ca723a05a456196347efbe855f712f68cf6b8a14f8f15" +dependencies = [ + "atty", + "base64 0.13.1", + "clap 2.34.0", + "encoding_rs", + "percent-encoding", + "url", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users 0.3.5", + "winapi", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.4", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "embed-resource" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54cc3e827ee1c3812239a9a41dede7b4d7d5d5464faa32d71bd7cba28ce2cb2" +dependencies = [ + "cc", + "rustc_version", + "toml 0.8.2", + "vswhom", + "winreg 0.51.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.0", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fdeflate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset 0.9.0", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib 0.18.4", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib 0.18.4", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib 0.18.4", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys 0.18.1", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.18.1", + "glib 0.18.4", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys 0.16.3", + "gobject-sys 0.16.3", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.16.3", + "glib-macros 0.16.8", + "glib-sys 0.16.3", + "gobject-sys 0.16.3", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e" +dependencies = [ + "bitflags 2.4.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.18.1", + "glib-macros 0.18.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glib-macros" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" +dependencies = [ + "heck", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys 0.16.3", + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib 0.18.4", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.10", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.10", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.11", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.10", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.51.1", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.3", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "infer" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" +dependencies = [ + "cfb", +] + +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" +dependencies = [ + "cfb", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib 0.18.4", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "treediff", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "jsonschema" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" +dependencies = [ + "ahash", + "anyhow", + "base64 0.21.5", + "bytecount", + "clap 4.4.11", + "fancy-regex", + "fraction", + "getrandom 0.2.11", + "iso8601", + "itoa 1.0.10", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.4.1", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib 0.18.4", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "value-bag", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac-notification-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64" +dependencies = [ + "cc", + "dirs-next", + "objc-foundation", + "objc_id", + "time", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minisign" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23ef13ff1d745b1e52397daaa247e333c607f3cff96d4df2b798dc252db974b" +dependencies = [ + "getrandom 0.2.11", + "rpassword", + "scrypt", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b564d551449738387fb4541aef5fbfceaa81b2b732f2534c1c7c89dc7d673eaa" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.1", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" +dependencies = [ + "crossbeam-channel", + "log", + "notify", +] + +[[package]] +name = "notify-rust" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827c5edfa80235ded4ab3fe8e9dc619b4f866ef16fe9b1c6b8a7f8692c0f2226" +dependencies = [ + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f" +dependencies = [ + "dlv-list", + "hashbrown 0.14.3", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + +[[package]] +name = "os_pipe" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib 0.18.4", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pest_meta" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plist" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" +dependencies = [ + "base64 0.21.5", + "indexmap 2.1.0", + "line-wrap", + "quick-xml 0.31.0", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.30", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "pretty_env_logger" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.16", + "redox_syscall 0.1.57", + "rust-argon2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.11", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.11", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "rfd" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241a0deb168c88050d872294f7b3106c1dfa8740942bcc97bc91b98e97b5c501" +dependencies = [ + "block", + "dispatch", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom 0.2.11", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.1", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" + +[[package]] +name = "rustls-webpki" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2 0.12.2", + "salsa20", + "sha2", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa 1.0.10", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa 1.0.10", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.10", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling 0.20.3", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4208d5a903276a9f3b797afdf6c5bc12a8da1344b053b100abf3565ecc80cb7e" +dependencies = [ + "bswap", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib 0.18.4", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "sval" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a2386bea23a121e4e72450306b1dd01078b6399af11b93897bf84640a28a59" + +[[package]] +name = "sval_buffer" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16c047898a0e19002005512243bc9ef1c1037aad7d03d6c594e234efec80795" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74fb116e2ecdcb280b0108aa2ee4434df50606c3208c47ac95432730eaac20c" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10837b4f0feccef271b2b1c03784e08f6d0bb6d23272ec9e8c777bfadbb8f1b8" +dependencies = [ + "itoa 1.0.10", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891f5ecdf34ce61a8ab2d10f9cfdc303347b0afec4dad6702757419d2d8312a9" +dependencies = [ + "itoa 1.0.10", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63fcffb4b79c531f38e3090788b64f3f4d54a180aacf02d69c42fa4e4bf284c3" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af725f9c2aa7cec4ca9c47da2cc90920c4c82d3fa537094c66c77a5459f5809d" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7589c649a03d21df40b9a926787d2c64937fa1dccec8d87c6cd82989a2e0a4" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + +[[package]] +name = "swift-rs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204" +dependencies = [ + "base64 0.21.5", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sys-locale" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +dependencies = [ + "libc", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0dff18fed076d29cb5779e918ef4b8a5dbb756204e4a027794f0bce233d949" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cocoa", + "core-foundation", + "core-graphics 0.23.1", + "crossbeam-channel", + "dispatch", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "image", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.52.0", + "windows-implement", + "windows-version", + "x11-dl", + "zbus", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "tauri" +version = "2.0.0-alpha.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05fb63873c39d3fd5ddad995d395e7b7394ece0b69aeacb31e91d24af48f3de1" +dependencies = [ + "anyhow", + "bytes", + "cocoa", + "dirs-next", + "embed_plist", + "futures-util", + "getrandom 0.2.11", + "glob", + "gtk", + "heck", + "http 0.2.11", + "ico", + "infer 0.15.0", + "jni", + "libc", + "log", + "mime", + "muda", + "objc", + "percent-encoding", + "png", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "state", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils 2.0.0-alpha.12", + "thiserror", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows 0.52.0", +] + +[[package]] +name = "tauri-build" +version = "2.0.0-alpha.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a2582ffb43e5c28932c43ffc40c295a9196a9a33ffb1163269c6baed84834a" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs-next", + "heck", + "json-patch", + "plist", + "semver", + "serde", + "serde_json", + "swift-rs", + "tauri-utils 2.0.0-alpha.12", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-bundler" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "657d0d0b2e820978ee51109bd75f03cee23dc1a83388d08f82fd368635c14e04" +dependencies = [ + "anyhow", + "ar", + "dirs-next", + "dunce", + "flate2", + "glob", + "handlebars", + "heck", + "hex", + "image", + "log", + "md5", + "os_pipe", + "plist", + "regex", + "semver", + "serde", + "serde_json", + "sha1", + "sha2", + "strsim 0.10.0", + "tar", + "tauri-icns", + "tauri-utils 1.5.3", + "tempfile", + "thiserror", + "time", + "ureq", + "uuid", + "walkdir", + "windows-sys 0.48.0", + "winreg 0.51.0", + "zip", +] + +[[package]] +name = "tauri-cli" +version = "1.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b2a88fd572b14c0ca224675aaad590e38d95e53846df55459edbdc910795eb" +dependencies = [ + "anyhow", + "axum", + "base64 0.21.5", + "cc", + "clap 4.4.11", + "clap_complete", + "colored", + "common-path", + "ctrlc", + "dialoguer", + "env_logger", + "glob", + "handlebars", + "heck", + "html5ever", + "ignore", + "image", + "include_dir", + "itertools", + "json-patch", + "jsonschema", + "kuchikiki", + "libc", + "log", + "minisign", + "notify", + "notify-debouncer-mini", + "once_cell", + "os_info", + "os_pipe", + "regex", + "semver", + "serde", + "serde-value", + "serde_json", + "shared_child", + "tauri-bundler", + "tauri-icns", + "tauri-utils 1.5.3", + "tokio", + "toml 0.8.2", + "toml_edit 0.21.1", + "unicode-width", + "ureq", + "url", + "winapi", + "zeroize", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.0-alpha.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06976ec7b704d6b842169ffd4ce596e9ce45917a0ab462cb96a119fa2829be9" +dependencies = [ + "base64 0.21.5", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "tauri-utils 2.0.0-alpha.12", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-icns" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b7eb4d0d43724ba9ba6a6717420ee68aee377816a3edbb45db8c18862b1431" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "tauri-macros" +version = "2.0.0-alpha.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff509be5a5ac34ec2e60d9029af1032c0a33e421f3e823bc92695192e2871c17" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.52", + "tauri-codegen", + "tauri-utils 2.0.0-alpha.12", +] + +[[package]] +name = "tauri-plugin-cli" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3ce9173dd3ae43c5c7529cce495e89cc9d8773adcd2a3e0efb5123aa052c64" +dependencies = [ + "clap 4.4.11", + "log", + "serde", + "serde_json", + "tauri", + "thiserror", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d537328bba01bcbbff4fc7daa1175744afdd42e554b6c897d9a1b1f76b023" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-build", + "thiserror", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ead9b1276ed45ffec0a27ff51239614fa9b462a7483f5cb98f0c555a40754e9" +dependencies = [ + "glib 0.16.9", + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-fs", + "thiserror", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c81479fdc92bab8609d896249e016404f9fac24a27ddf66e1daafd4db1a35" +dependencies = [ + "anyhow", + "glob", + "serde", + "tauri", + "thiserror", + "uuid", +] + +[[package]] +name = "tauri-plugin-notification" +version = "2.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e1cfe331495d0e72b9d48191eec98a54f9e189571b8ec6affb39b90b3df3bc" +dependencies = [ + "log", + "notify-rust", + "rand 0.8.5", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-build", + "thiserror", + "time", + "url", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dfd82d59cdf0229ffe62d38e12bdfee053c4f915883afe6f982b672a7e28d44" +dependencies = [ + "gethostname 0.4.3", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "thiserror", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf84ccb3f5ac4df2dfeb5e2f09b9048d8633d9b98d72c701aba72642790f2d9" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "serde", + "serde_json", + "shared_child", + "tauri", + "thiserror", +] + +[[package]] +name = "tauri-plugin-single-instance" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d25b229dea0a7cb72ab43ebd17fa7479eda058678bead1ecca431013d5e5ebf" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "thiserror", + "windows-sys 0.52.0", + "zbus", +] + +[[package]] +name = "tauri-runtime" +version = "1.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a989e58af6e554dbac798a0a8d112faafc1509bcfab626466181e0724f09c5" +dependencies = [ + "gtk", + "http 0.2.11", + "jni", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils 2.0.0-alpha.12", + "thiserror", + "url", + "windows 0.52.0", +] + +[[package]] +name = "tauri-runtime-wry" +version = "1.0.0-alpha.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9f181a6f5f982204ae293c19f37ba90116b8ec0bfd0a08c7a7ba67200cd9e3" +dependencies = [ + "cocoa", + "gtk", + "http 0.2.11", + "jni", + "percent-encoding", + "raw-window-handle", + "tao", + "tauri-runtime", + "tauri-utils 2.0.0-alpha.12", + "webkit2gtk", + "webview2-com", + "windows 0.52.0", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ad0bbb31fccd1f4c56275d0a5c3abdf1f59999f72cb4ef8b79b4ed42082a21" +dependencies = [ + "aes-gcm", + "ctor", + "dunce", + "getrandom 0.2.11", + "glob", + "heck", + "html5ever", + "infer 0.13.0", + "json-patch", + "json5", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "schemars", + "semver", + "serde", + "serde_json", + "serde_with", + "serialize-to-javascript", + "thiserror", + "toml 0.7.8", + "url", + "walkdir", + "windows-version", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-alpha.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4858f99fc9f28b72008ef51d04d18b7e3646845c2bc18ee340045fed6ed5095" +dependencies = [ + "brotli", + "ctor", + "dunce", + "glob", + "heck", + "html5ever", + "infer 0.15.0", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.8", +] + +[[package]] +name = "tauri-winrt-notification" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006851c9ccefa3c38a7646b8cec804bb429def3da10497bfa977179869c3e8e2" +dependencies = [ + "quick-xml 0.30.0", + "windows 0.51.1", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.30", + "windows-sys 0.48.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa 1.0.10", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.5", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tokio-websockets" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b069bad86dda43d908b4221fe04fe49d2ed8e0a24d319a5c6a8d250e76fe15b" +dependencies = [ + "base64 0.21.5", + "bytes", + "futures-core", + "futures-sink", + "http 1.0.0", + "httparse", + "rand 0.8.5", + "ring", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tray-icon" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad962d06d2bfd9b2ab4f665fc73b175523b834b1466a294520201c5845145f8" +dependencies = [ + "cocoa", + "core-graphics 0.23.1", + "crossbeam-channel", + "dirs-next", + "libappindicator", + "muda", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "treediff" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +dependencies = [ + "serde_json", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 0.2.11", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.0", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +dependencies = [ + "base64 0.21.5", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "rustls-webpki", + "socks", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom 0.2.11", + "sha1_smol", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc35703541cbccb5278ef7b589d79439fc808ff0b5867195a3230f9a47421d39" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285b43c29d0b4c0e65aad24561baee67a1b69dc9be9375d4a85138cbf556f7f8" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys 0.18.1", + "glib 0.18.4", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.52.0", + "windows-core 0.52.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows 0.52.0", + "windows-core 0.52.0", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "which" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.30", + "windows-sys 0.52.0", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6abc2b9c56bd95887825a1ce56cde49a2a97c07e28db465d541f5098a2656c" +dependencies = [ + "cocoa", + "objc", + "raw-window-handle", + "windows-sys 0.52.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core 0.51.1", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement", + "windows-interface", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-service" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8" +dependencies = [ + "bitflags 1.3.2", + "widestring", + "windows-sys 0.45.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ad1bc1d6925e0cde1bd01830b0073cd0448e21357e843b9ede33b6d81c7423" +dependencies = [ + "base64 0.21.5", + "block", + "cfg_aliases", + "cocoa", + "core-graphics 0.23.1", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.11", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "raw-window-handle", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.52.0", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" +dependencies = [ + "gethostname 0.3.0", + "nix 0.26.4", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" +dependencies = [ + "nix 0.26.4", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.12", + "rustix 0.38.30", +] + +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix 0.26.4", + "winapi", +] + +[[package]] +name = "zbus" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2 0.11.0", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/desktop/tauri/src-tauri/Cargo.toml b/desktop/tauri/src-tauri/Cargo.toml new file mode 100644 index 00000000..6fed0ec3 --- /dev/null +++ b/desktop/tauri/src-tauri/Cargo.toml @@ -0,0 +1,75 @@ +[package] +name = "app" +version = "0.1.0" +description = "Portmaster UI" +authors = ["Safing"] +license = "" +repository = "" +default-run = "app" +edition = "2021" +rust-version = "1.60" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "2.0.0-alpha", features = [] } + +[dependencies] +# Tauri +tauri = { version = "2.0.0-alpha", features = ["tray-icon", "icon-ico", "icon-png"] } +tauri-plugin-shell = "2.0.0-alpha" +tauri-plugin-dialog = "2.0.0-alpha" +tauri-plugin-clipboard-manager = "2.0.0-alpha" +tauri-plugin-os = "2.0.0-alpha" +tauri-plugin-single-instance = "2.0.0-alpha" +tauri-plugin-cli = "2.0.0-alpha" +tauri-plugin-notification = "2.0.0-alpha" + +# We still need the tauri-cli 1.5 for building +tauri-cli = "1.5.11" + +# General +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +futures-util = { version = "0.3", features = ["sink"] } +dirs = "1.0" +rust-ini = "0.20.0" +dataurl = "0.1.2" +uuid = "1.6.1" +lazy_static = "1.4.0" +tokio = { version = "1.35.0", features = ["macros"] } +cached = "0.46.1" +notify-rust = "4.10.0" +assert_matches = "1.5.0" +tokio-websockets = { version = "0.5.0", features = ["client", "ring", "rand"] } +sha = "1.0.3" +http = "1.0.0" +url = "2.5.0" +thiserror = "1.0" +log = "0.4.21" +pretty_env_logger = "0.5.0" + +# Linux only +[target.'cfg(target_os = "linux")'.dependencies] +glib = "0.18.4" +gtk-sys = "0.18.0" +glib-sys = "0.18.1" +gdk-pixbuf = "0.18.3" +gdk-pixbuf-sys = "0.18.0" +gio-sys = "0.18.1" + +# Windows only +[target.'cfg(target_os = "windows")'.dependencies] +windows-service = "0.6.0" +windows = { version = "0.54.0", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } + +[dev-dependencies] +which = "6.0.0" +gtk = "0.18" +ctor = "0.2.6" + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. +# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes. +# DO NOT REMOVE!! +custom-protocol = [ "tauri/custom-protocol" ] diff --git a/desktop/tauri/src-tauri/build.rs b/desktop/tauri/src-tauri/build.rs new file mode 100644 index 00000000..795b9b7c --- /dev/null +++ b/desktop/tauri/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/desktop/tauri/src-tauri/src/main.rs b/desktop/tauri/src-tauri/src/main.rs new file mode 100644 index 00000000..2b1def20 --- /dev/null +++ b/desktop/tauri/src-tauri/src/main.rs @@ -0,0 +1,204 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use tauri::{AppHandle, Manager, RunEvent, WindowEvent}; +use tauri_plugin_cli::CliExt; + +// Library crates +mod portapi; +mod service; + +#[cfg(target_os = "linux")] +mod xdg; + +// App modules +mod portmaster; +mod traymenu; +mod window; + +use log::{debug, error, info, trace, warn}; +use portmaster::PortmasterExt; +use traymenu::setup_tray_menu; +use window::{close_splash_window, create_main_window}; + +#[macro_use] +extern crate lazy_static; + +#[derive(Clone, serde::Serialize)] +struct Payload { + args: Vec<String>, + cwd: String, +} + +struct WsHandler { + handle: AppHandle, + background: bool, + + is_first_connect: bool, +} + +impl portmaster::Handler for WsHandler { + fn on_connect(&mut self, cli: portapi::client::PortAPI) -> () { + // we successfully connected to Portmaster. Set is_first_connect to false + // so we don't show the splash-screen when we loose connection. + self.is_first_connect = false; + + if let Err(err) = close_splash_window(&self.handle) { + error!("failed to close splash window: {}", err.to_string()); + } + + // create the main window now. It's not automatically visible by default. + // Rather, the angular application will show the window itself when it finished + // bootstrapping. + if let Err(err) = create_main_window(&self.handle) { + error!("failed to create main window: {}", err.to_string()); + } + + let handle = self.handle.clone(); + tauri::async_runtime::spawn(async move { + traymenu::tray_handler(cli, handle).await; + }); + } + + fn on_disconnect(&mut self) { + // if we're not running in background and this was the first connection attempt + // then display the splash-screen. + // + // Once we had a successful connection the splash-screen will not be shown anymore + // since there's already a main window with the angular application. + if !self.background && self.is_first_connect { + let _ = window::create_splash_window(&self.handle.clone()); + + self.is_first_connect = false + } + } +} + +fn main() { + pretty_env_logger::init(); + + let app = tauri::Builder::default() + // Shell plugin for open_external support + .plugin(tauri_plugin_shell::init()) + // Clipboard support + .plugin(tauri_plugin_clipboard_manager::init()) + // Dialog (Save/Open) support + .plugin(tauri_plugin_dialog::init()) + // OS Version and Architecture support + .plugin(tauri_plugin_os::init()) + // Single instance guard + .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { + let _ = app.emit("single-instance", Payload { args: argv, cwd }); + })) + // Custom CLI arguments + .plugin(tauri_plugin_cli::init()) + // Notification support + .plugin(tauri_plugin_notification::init()) + // Our Portmaster Plugin that handles communication between tauri and our angular app. + .plugin(portmaster::init()) + // Setup the app an any listeners + .setup(|app| { + setup_tray_menu(app)?; + + // Setup the single-instance event listener that will create/focus the main window + // or the splash-screen. + let handle = app.handle().clone(); + app.listen_global("single-instance", move |_event| { + let _ = window::open_window(&handle); + }); + + // Handle cli flags: + // + let mut background = false; + match app.cli().matches() { + Ok(matches) => { + debug!("cli matches={:?}", matches); + + if let Some(bg_flag) = matches.args.get("background") { + match bg_flag.value.as_bool() { + Some(value) => { + background = value; + app.portmaster().set_show_after_bootstrap(!background); + } + None => {} + } + } + + if let Some(nf_flag) = matches.args.get("with-notifications") { + match nf_flag.value.as_bool() { + Some(v) => { + app.portmaster().with_notification_support(v); + } + None => {} + } + } + + if let Some(pf_flag) = matches.args.get("with-prompts") { + match pf_flag.value.as_bool() { + Some(v) => { + app.portmaster().with_connection_prompts(v); + } + None => {} + } + } + } + Err(err) => { + error!("failed to parse cli arguments: {}", err.to_string()); + } + }; + + // prepare a custom portmaster plugin handler that will show the splash-screen + // (if not in --background) and launch the tray-icon handler. + let handler = WsHandler { + handle: app.handle().clone(), + background, + is_first_connect: true, + }; + + // register the custom handler + app.portmaster().register_handler(handler); + + Ok(()) + }) + .any_thread() + .build(tauri::generate_context!()) + .expect("error while running tauri application"); + + app.run(|handle, e| match e { + RunEvent::WindowEvent { label, event, .. } => { + if label != "main" { + // We only have one window at most so any other label is unexpected + return; + } + + // Do not let the user close the window, instead send an event to the main + // window so we can show the "will not stop portmaster" dialog and let the window + // close itself using + // + // window.__TAURI__.window.getCurrent().close() + // + // Note: the above javascript does NOT trigger the CloseRequested event so + // there's no need to handle that case here. + // + match event { + WindowEvent::CloseRequested { api, .. } => { + debug!( + "window (label={}) close request received, forwarding to user-interface.", + label + ); + + api.prevent_close(); + if let Some(window) = handle.get_window(label.as_str()) { + let _ = window.emit("exit-requested", ""); + } + } + _ => {} + } + } + + RunEvent::ExitRequested { api, .. } => { + api.prevent_exit(); + } + _ => {} + }); +} diff --git a/desktop/tauri/src-tauri/src/portapi/client.rs b/desktop/tauri/src-tauri/src/portapi/client.rs new file mode 100644 index 00000000..b3b00d09 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/client.rs @@ -0,0 +1,191 @@ +use futures_util::{SinkExt, StreamExt}; +use http::Uri; +use log::{debug, error, warn}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicUsize, Ordering}; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::RwLock; +use tokio_websockets::{ClientBuilder, Error}; + +use super::message::*; +use super::types::*; + +/// An internal representation of a Command that +/// contains the PortAPI message as well as a response +/// channel that will receive all responses sent from the +/// server. +/// +/// Users should normally not need to use the Command struct +/// directly since `PortAPI` already abstracts the creation of +/// mpsc channels. +struct Command { + msg: Message, + response: Sender<Response>, +} + +/// The client implementation for PortAPI. +#[derive(Clone)] +pub struct PortAPI { + dispatch: Sender<Command>, +} + +/// The map type used to store message subscribers. +type SubscriberMap = RwLock<HashMap<usize, Sender<Response>>>; + +/// Connect to PortAPI at the specified URI. +/// +/// This method will launch a new async thread on the `tauri::async_runtime` +/// that will handle message to transmit and also multiplex server responses +/// to the appropriate subscriber. +pub async fn connect(uri: &str) -> Result<PortAPI, Error> { + let parsed = match uri.parse::<Uri>() { + Ok(u) => u, + Err(_e) => { + return Err(Error::NoUriConfigured); // TODO(ppacher): fix the return error type. + } + }; + + let (mut client, _) = ClientBuilder::from_uri(parsed).connect().await?; + let (tx, mut dispatch) = channel::<Command>(64); + + tauri::async_runtime::spawn(async move { + let subscribers: SubscriberMap = RwLock::new(HashMap::new()); + let next_id = AtomicUsize::new(0); + + loop { + tokio::select! { + msg = client.next() => { + let msg = match msg { + Some(msg) => msg, + None => { + warn!("websocket connection lost"); + + dispatch.close(); + return; + } + }; + + match msg { + Err(err) => { + error!("failed to receive frame from websocket: {}", err); + + dispatch.close(); + return; + }, + Ok(msg) => { + let text = unsafe { + std::str::from_utf8_unchecked(msg.as_payload()) + }; + + match text.parse::<Message>() { + Ok(msg) => { + let id = msg.id; + let map = subscribers + .read() + .await; + + if let Some(sub) = map.get(&id) { + let res: Result<Response, MessageError> = msg.try_into(); + match res { + Ok(response) => { + if let Err(err) = sub.send(response).await { + // The receiver side has been closed already, + // drop the read lock and remove the subscriber + // from our hashmap + drop(map); + + subscribers + .write() + .await + .remove(&id); + + debug!("subscriber for command {} closed read side: {}", id, err); + } + }, + Err(err) => { + error!("invalid command: {}", err); + } + } + } + }, + Err(err) => { + error!("failed to deserialize message: {}", err) + } + } + } + } + + }, + + Some(mut cmd) = dispatch.recv() => { + let id = next_id.fetch_add(1, Ordering::Relaxed); + cmd.msg.id = id; + let blob: String = cmd.msg.into(); + + debug!("Sending websocket frame: {}", blob); + + match client.send(tokio_websockets::Message::text(blob)).await { + Ok(_) => { + subscribers + .write() + .await + .insert(id, cmd.response); + }, + Err(err) => { + error!("failed to dispatch command: {}", err); + + // TODO(ppacher): we should send some error to cmd.response here. + // Otherwise, the sender of cmd might get stuck waiting for responses + // if they don't check for PortAPI.is_closed(). + + return + } + } + } + } + } + }); + + Ok(PortAPI { dispatch: tx }) +} + +impl PortAPI { + /// `request` sends a PortAPI `portapi::types::Request` to the server and returns a mpsc receiver channel + /// where all server responses are forwarded. + /// + /// If the caller does not intend to read any responses the returned receiver may be closed or + /// dropped. As soon as the async-thread launched in `connect` detects a closed receiver it is remove + /// from the subscription map. + /// + /// The default buffer size for the channel is 64. Use `request_with_buffer_size` to specify a dedicated buffer size. + pub async fn request( + &self, + r: Request, + ) -> std::result::Result<Receiver<Response>, MessageError> { + self.request_with_buffer_size(r, 64).await + } + + // Like `request` but supports explicitly specifying a channel buffer size. + pub async fn request_with_buffer_size( + &self, + r: Request, + buffer: usize, + ) -> std::result::Result<Receiver<Response>, MessageError> { + let (tx, rx) = channel(buffer); + + let msg: Message = r.try_into()?; + + let _ = self.dispatch.send(Command { response: tx, msg }).await; + + Ok(rx) + } + + /// Reports whether or not the websocket connection to the Portmaster Database API has been closed + /// due to errors. + /// + /// Users are expected to check this field on a regular interval to detect any issues and perform + /// a clean re-connect by calling `connect` again. + pub fn is_closed(&self) -> bool { + self.dispatch.is_closed() + } +} diff --git a/desktop/tauri/src-tauri/src/portapi/message.rs b/desktop/tauri/src-tauri/src/portapi/message.rs new file mode 100644 index 00000000..46eb7c77 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/message.rs @@ -0,0 +1,258 @@ +use thiserror::Error; + +/// MessageError describes any error that is encountered when parsing +/// PortAPI messages or when converting between the Request/Response types. +#[derive(Debug, Error)] +pub enum MessageError { + #[error("missing command id")] + MissingID, + + #[error("invalid command id")] + InvalidID, + + #[error("missing command")] + MissingCommand, + + #[error("missing key")] + MissingKey, + + #[error("missing payload")] + MissingPayload, + + #[error("unknown or unsupported command: {0}")] + UnknownCommand(String), + + #[error(transparent)] + InvalidPayload(#[from] serde_json::Error), +} + + +/// Payload defines the payload type and content of a PortAPI message. +/// +/// For the time being, only JSON payloads (indicated by a prefixed 'J' of the payload content) +/// is directly supported in `Payload::parse()`. +/// +/// For other payload types (like CBOR, BSON, ...) it's the user responsibility to figure out +/// appropriate decoding from the `Payload::UNKNOWN` variant. +#[derive(PartialEq, Debug, Clone)] +pub enum Payload { + JSON(String), + UNKNOWN(String), +} + +/// ParseError is returned from `Payload::parse()`. +#[derive(Debug, Error)] +pub enum ParseError { + #[error(transparent)] + JSON(#[from] serde_json::Error), + + #[error("unknown error while parsing")] + UNKNOWN +} + + +impl Payload { + /// Parse the payload into T. + /// + /// Only JSON parsing is supported for now. See [Payload] for more information. + pub fn parse<'a, T>(self: &'a Self) -> std::result::Result<T, ParseError> + where + T: serde::de::Deserialize<'a> { + + match self { + Payload::JSON(blob) => Ok(serde_json::from_str::<T>(blob.as_str())?), + Payload::UNKNOWN(_) => Err(ParseError::UNKNOWN), + } + } +} + +/// Supports creating a Payload instance from a String. +/// +/// See [Payload] for more information. +impl std::convert::From<String> for Payload { + fn from(value: String) -> Payload { + let mut chars = value.chars(); + let first = chars.next(); + let rest = chars.as_str().to_string(); + + match first { + Some(c) => match c { + 'J' => Payload::JSON(rest), + _ => Payload::UNKNOWN(value), + }, + None => Payload::UNKNOWN("".to_string()) + } + } +} + +/// Display implementation for Payload that just displays the raw payload. +impl std::fmt::Display for Payload { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Payload::JSON(payload) => { + write!(f, "J{}", payload) + }, + Payload::UNKNOWN(payload) => { + write!(f, "{}", payload) + } + } + } +} + +/// Message is an internal representation of a PortAPI message. +/// Users should more likely use `portapi::types::Request` and `portapi::types::Response` +/// instead of directly using `Message`. +/// +/// The struct is still public since it might be useful for debugging or to implement new +/// commands not yet supported by the `portapi::types` crate. +#[derive(PartialEq, Debug, Clone)] +pub struct Message { + pub id: usize, + pub cmd: String, + pub key: Option<String>, + pub payload: Option<Payload>, +} + +/// Implementation to marshal a PortAPI message into it's wire-format representation +/// (which is a string). +/// +/// Note that this conversion does not check for invalid messages! +impl std::convert::From<Message> for String { + fn from(value: Message) -> Self { + let mut result = "".to_owned(); + + result.push_str(value.id.to_string().as_str()); + result.push_str("|"); + result.push_str(&value.cmd); + + if let Some(key) = value.key { + result.push_str("|"); + result.push_str(key.as_str()); + } + + if let Some(payload) = value.payload { + result.push_str("|"); + result.push_str(payload.to_string().as_str()) + } + + result + } +} + +/// An implementation for `String::parse()` to convert a wire-format representation +/// of a PortAPI message to a Message instance. +/// +/// Any errors returned from `String::parse()` will be of type `MessageError` +impl std::str::FromStr for Message { + type Err = MessageError; + + fn from_str(line: &str) -> Result<Self, Self::Err> { + let parts = line.split("|").collect::<Vec<&str>>(); + + let id = match parts.get(0) { + Some(s) => match (*s).parse::<usize>() { + Ok(id) => Ok(id), + Err(_) => Err(MessageError::InvalidID), + }, + None => Err(MessageError::MissingID), + }?; + + let cmd = match parts.get(1) { + Some(s) => Ok(*s), + None => Err(MessageError::MissingCommand), + }? + .to_string(); + + let key = parts.get(2) + .and_then(|key| Some(key.to_string())); + + let payload : Option<Payload> = parts.get(3) + .and_then(|p| Some(p.to_string().into())); + + return Ok(Message { + id, + cmd, + key, + payload: payload + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Deserialize; + + #[derive(Debug, PartialEq, Deserialize)] + struct Test { + a: i64, + s: String, + } + + #[test] + fn payload_to_string() { + let p = Payload::JSON("{}".to_string()); + assert_eq!(p.to_string(), "J{}"); + + let p = Payload::UNKNOWN("some unknown content".to_string()); + assert_eq!(p.to_string(), "some unknown content"); + } + + #[test] + fn payload_from_string() { + let p: Payload = "J{}".to_string().into(); + assert_eq!(p, Payload::JSON("{}".to_string())); + + let p: Payload = "some unknown content".to_string().into(); + assert_eq!(p, Payload::UNKNOWN("some unknown content".to_string())); + } + + #[test] + fn payload_parse() { + let p: Payload = "J{\"a\": 100, \"s\": \"string\"}".to_string().into(); + + let t: Test = p.parse() + .expect("Expected payload parsing to work"); + + assert_eq!(t, Test{ + a: 100, + s: "string".to_string(), + }); + } + + #[test] + fn parse_message() { + let m = "10|insert|some:key|J{}".parse::<Message>() + .expect("Expected message to parse"); + + assert_eq!(m, Message{ + id: 10, + cmd: "insert".to_string(), + key: Some("some:key".to_string()), + payload: Some(Payload::JSON("{}".to_string())), + }); + + let m = "1|done".parse::<Message>() + .expect("Expected message to parse"); + + assert_eq!(m, Message{ + id: 1, + cmd: "done".to_string(), + key: None, + payload: None + }); + + let m = "".parse::<Message>() + .expect_err("Expected parsing to fail"); + if let MessageError::InvalidID = m {} else { + panic!("unexpected error value: {}", m) + } + + let m = "1".parse::<Message>() + .expect_err("Expected parsing to fail"); + + if let MessageError::MissingCommand = m {} else { + panic!("unexpected error value: {}", m) + } + } +} diff --git a/desktop/tauri/src-tauri/src/portapi/mod.rs b/desktop/tauri/src-tauri/src/portapi/mod.rs new file mode 100644 index 00000000..67fd2710 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/mod.rs @@ -0,0 +1,4 @@ +pub mod client; +pub mod message; +pub mod types; +pub mod models; \ No newline at end of file diff --git a/desktop/tauri/src-tauri/src/portapi/models/config.rs b/desktop/tauri/src-tauri/src/portapi/models/config.rs new file mode 100644 index 00000000..e29474a8 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/models/config.rs @@ -0,0 +1,18 @@ +use serde::*; +use super::super::message::Payload; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct BooleanValue { + #[serde(rename = "Value")] + pub value: Option<bool>, +} + +impl TryInto<Payload> for BooleanValue { + type Error = serde_json::Error; + + fn try_into(self) -> Result<Payload, Self::Error> { + let str = serde_json::to_string(&self)?; + + Ok(Payload::JSON(str)) + } +} \ No newline at end of file diff --git a/desktop/tauri/src-tauri/src/portapi/models/mod.rs b/desktop/tauri/src-tauri/src/portapi/models/mod.rs new file mode 100644 index 00000000..91336dd0 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/models/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +pub mod spn; +pub mod notification; +pub mod subsystem; \ No newline at end of file diff --git a/desktop/tauri/src-tauri/src/portapi/models/notification.rs b/desktop/tauri/src-tauri/src/portapi/models/notification.rs new file mode 100644 index 00000000..51f4ece4 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/models/notification.rs @@ -0,0 +1,70 @@ +use serde::*; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct Notification { + #[serde(rename = "EventID")] + pub event_id: String, + + #[serde(rename = "GUID")] + pub guid: String, + + #[serde(rename = "Type")] + pub notification_type: NotificationType, + + #[serde(rename = "Message")] + pub message: String, + + #[serde(rename = "Title")] + pub title: String, + #[serde(rename = "Category")] + pub category: String, + + #[serde(rename = "EventData")] + pub data: serde_json::Value, + + #[serde(rename = "Expires")] + pub expires: u64, + + #[serde(rename = "State")] + pub state: String, + + #[serde(rename = "AvailableActions")] + pub actions: Vec<Action>, + + #[serde(rename = "SelectedActionID")] + pub selected_action_id: String, + + #[serde(rename = "ShowOnSystem")] + pub show_on_system: bool, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct Action { + #[serde(rename = "ID")] + pub id: String, + + #[serde(rename = "Text")] + pub text: String, + + #[serde(rename = "Type")] + pub action_type: String, + + #[serde(rename = "Payload")] + pub payload: serde_json::Value, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct NotificationType(i32); + +#[allow(dead_code)] +pub const INFO: NotificationType = NotificationType(0); + +#[allow(dead_code)] +pub const WARN: NotificationType = NotificationType(1); + +#[allow(dead_code)] +pub const PROMPT: NotificationType = NotificationType(2); + +#[allow(dead_code)] +pub const ERROR: NotificationType = NotificationType(3); + diff --git a/desktop/tauri/src-tauri/src/portapi/models/spn.rs b/desktop/tauri/src-tauri/src/portapi/models/spn.rs new file mode 100644 index 00000000..549c2e27 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/models/spn.rs @@ -0,0 +1,8 @@ +use serde::*; + + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct SPNStatus { + #[serde(rename = "Status")] + pub status: String, +} \ No newline at end of file diff --git a/desktop/tauri/src-tauri/src/portapi/models/subsystem.rs b/desktop/tauri/src-tauri/src/portapi/models/subsystem.rs new file mode 100644 index 00000000..c8b0ea27 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/models/subsystem.rs @@ -0,0 +1,45 @@ +#![allow(dead_code)] +use serde::*; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct ModuleStatus { + #[serde(rename = "Name")] + pub name: String, + + #[serde(rename = "Enabled")] + pub enabled: bool, + + #[serde(rename = "Status")] + pub status: u8, + + #[serde(rename = "FailureStatus")] + pub failure_status: u8, + + #[serde(rename = "FailureID")] + pub failure_id: String, + + #[serde(rename = "FailureMsg")] + pub failure_msg: String, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct Subsystem { + #[serde(rename = "ID")] + pub id: String, + + #[serde(rename = "Name")] + pub name: String, + + #[serde(rename = "Description")] + pub description: String, + + #[serde(rename = "Modules")] + pub module_status: Vec<ModuleStatus>, + + #[serde(rename = "FailureStatus")] + pub failure_status: u8, +} +pub const FAILURE_NONE: u8 = 0; +pub const FAILURE_HINT: u8 = 1; +pub const FAILURE_WARNING: u8 = 2; +pub const FAILURE_ERROR: u8 = 3; diff --git a/desktop/tauri/src-tauri/src/portapi/types.rs b/desktop/tauri/src-tauri/src/portapi/types.rs new file mode 100644 index 00000000..632f24f4 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portapi/types.rs @@ -0,0 +1,199 @@ + +use super::message::*; + +/// Request is a strongly typed request message +/// that can be converted to a `portapi::message::Message` +/// object for further use by the client (`portapi::client::PortAPI`). +#[derive(PartialEq, Debug)] +pub enum Request { + Get(String), + Query(String), + Subscribe(String), + QuerySubscribe(String), + Create(String, Payload), + Update(String, Payload), + Insert(String, Payload), + Delete(String), + Cancel, +} + +/// Implementation to convert a internal `portapi::message::Message` to a valid +/// `Request` variant. +/// +/// Any error returned will be of type `portapi::message::MessageError`. +impl std::convert::TryFrom<Message> for Request { + type Error = MessageError; + + fn try_from(value: Message) -> Result<Self, Self::Error> { + match value.cmd.as_str() { + "get" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + Ok(Request::Get(key)) + }, + "query" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + Ok(Request::Query(key)) + }, + "sub" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + Ok(Request::Subscribe(key)) + }, + "qsub" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + Ok(Request::QuerySubscribe(key)) + }, + "create" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + Ok(Request::Create(key, payload)) + }, + "update" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + Ok(Request::Update(key, payload)) + }, + "insert" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + Ok(Request::Insert(key, payload)) + }, + "delete" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + Ok(Request::Delete(key)) + }, + "cancel" => { + Ok(Request::Cancel) + }, + cmd => { + Err(MessageError::UnknownCommand(cmd.to_string())) + } + } + } +} + +/// An implementation to try to convert a `Request` variant into a valid +/// `portapi::message::Message` struct. +/// +/// While this implementation does not yet return any errors, it's expected that +/// additional validation will be added in the future so users should already expect +/// to receive `portapi::message::MessageError`s. +impl std::convert::TryFrom<Request> for Message { + type Error = MessageError; + + fn try_from(value: Request) -> Result<Self, Self::Error> { + match value { + Request::Get(key) => Ok(Message { id: 0, cmd: "get".to_string(), key: Some(key), payload: None }), + Request::Query(key) => Ok(Message { id: 0, cmd: "query".to_string(), key: Some(key), payload: None }), + Request::Subscribe(key) => Ok(Message { id: 0, cmd: "sub".to_string(), key: Some(key), payload: None }), + Request::QuerySubscribe(key) => Ok(Message { id: 0, cmd: "qsub".to_string(), key: Some(key), payload: None }), + Request::Create(key, value) => Ok(Message{ id: 0, cmd: "create".to_string(), key: Some(key), payload: Some(value)}), + Request::Update(key, value) => Ok(Message{ id: 0, cmd: "update".to_string(), key: Some(key), payload: Some(value)}), + Request::Insert(key, value) => Ok(Message{ id: 0, cmd: "insert".to_string(), key: Some(key), payload: Some(value)}), + Request::Delete(key) => Ok(Message { id: 0, cmd: "delete".to_string(), key: Some(key), payload: None }), + Request::Cancel => Ok(Message { id: 0, cmd: "cancel".to_string(), key: None, payload: None }), + } + } +} + + +/// Response is strongly types PortAPI response message. +/// that can be converted to a `portapi::message::Message` +/// object for further use by the client (`portapi::client::PortAPI`). +#[derive(PartialEq, Debug)] +pub enum Response { + Ok(String, Payload), + Update(String, Payload), + New(String, Payload), + Delete(String), + Success, + Error(String), + Warning(String), + Done +} + +/// Implementation to convert a internal `portapi::message::Message` to a valid +/// `Response` variant. +/// +/// Any error returned will be of type `portapi::message::MessageError`. +impl std::convert::TryFrom<Message> for Response { + type Error = MessageError; + + fn try_from(value: Message) -> Result<Self, MessageError> { + match value.cmd.as_str() { + "ok" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + + Ok(Response::Ok(key, payload)) + }, + "upd" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + + Ok(Response::Update(key, payload)) + }, + "new" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + let payload = value.payload.ok_or(MessageError::MissingPayload)?; + + Ok(Response::New(key, payload)) + }, + "del" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + + Ok(Response::Delete(key)) + }, + "success" => { + Ok(Response::Success) + }, + "error" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + + Ok(Response::Error(key)) + }, + "warning" => { + let key = value.key.ok_or(MessageError::MissingKey)?; + + Ok(Response::Warning(key)) + }, + "done" => { + Ok(Response::Done) + }, + cmd => Err(MessageError::UnknownCommand(cmd.to_string())) + } + } +} + +/// An implementation to try to convert a `Response` variant into a valid +/// `portapi::message::Message` struct. +/// +/// While this implementation does not yet return any errors, it's expected that +/// additional validation will be added in the future so users should already expect +/// to receive `portapi::message::MessageError`s. +impl std::convert::TryFrom<Response> for Message { + type Error = MessageError; + + fn try_from(value: Response) -> Result<Self, Self::Error> { + match value { + Response::Ok(key, payload) => Ok(Message{id: 0, cmd: "ok".to_string(), key: Some(key), payload: Some(payload)}), + Response::Update(key, payload) => Ok(Message{id: 0, cmd: "upd".to_string(), key: Some(key), payload: Some(payload)}), + Response::New(key, payload) => Ok(Message{id: 0, cmd: "new".to_string(), key: Some(key), payload: Some(payload)}), + Response::Delete(key ) => Ok(Message{id: 0, cmd: "del".to_string(), key: Some(key), payload: None}), + Response::Success => Ok(Message{id: 0, cmd: "success".to_string(), key: None, payload: None}), + Response::Warning(key) => Ok(Message{id: 0, cmd: "warning".to_string(), key: Some(key), payload: None}), + Response::Error(key) => Ok(Message{id: 0, cmd: "error".to_string(), key: Some(key), payload: None}), + Response::Done => Ok(Message{id: 0, cmd: "done".to_string(), key: None, payload: None}), + } + } +} + + +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub struct Record { + pub created: u64, + pub deleted: u64, + pub expires: u64, + pub modified: u64, + pub key: String, +} \ No newline at end of file diff --git a/desktop/tauri/src-tauri/src/portmaster/commands.rs b/desktop/tauri/src-tauri/src/portmaster/commands.rs new file mode 100644 index 00000000..dfa2b222 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portmaster/commands.rs @@ -0,0 +1,182 @@ +use super::PortmasterPlugin; +use crate::service::get_service_manager; +use crate::service::ServiceManager; +use log::debug; +use std::sync::atomic::Ordering; +use tauri::{Manager, Runtime, State, Window}; + +pub type Result = std::result::Result<String, String>; + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +pub struct Error { + pub error: String, +} + +#[tauri::command] +pub fn should_show<R: Runtime>( + _window: Window<R>, + portmaster: State<'_, PortmasterPlugin<R>>, +) -> Result { + if portmaster.get_show_after_bootstrap() { + debug!("[tauri:rpc:should_show] application should show after bootstrap"); + + Ok("show".to_string()) + } else { + debug!("[tauri:rpc:should_show] application should hide after bootstrap"); + + Ok("hide".to_string()) + } +} + +#[tauri::command] +pub fn should_handle_prompts<R: Runtime>( + _window: Window<R>, + portmaster: State<'_, PortmasterPlugin<R>>, +) -> Result { + if portmaster.handle_prompts.load(Ordering::Relaxed) { + Ok("true".to_string()) + } else { + Ok("false".to_string()) + } +} + +#[tauri::command] +pub fn get_state<R: Runtime>( + _window: Window<R>, + portmaster: State<'_, PortmasterPlugin<R>>, + key: String, +) -> Result { + let value = portmaster.get_state(key); + + if let Some(value) = value { + Ok(value) + } else { + Ok("".to_string()) + } +} + +#[tauri::command] +pub fn set_state<R: Runtime>( + _window: Window<R>, + portmaster: State<'_, PortmasterPlugin<R>>, + key: String, + value: String, +) -> Result { + portmaster.set_state(key, value); + + Ok("".to_string()) +} + +#[cfg(target_os = "linux")] +#[tauri::command] +pub fn get_app_info<R: Runtime>( + window: Window<R>, + response_id: String, + matching_path: String, + exec_path: String, + pid: i64, + cmdline: String, +) -> Result { + let mut id = response_id; + + let info = crate::xdg::ProcessInfo { + cmdline, + exec_path, + pid, + matching_path, + }; + + if id == "" { + id = uuid::Uuid::new_v4().to_string() + } + let cloned = id.clone(); + + std::thread::spawn(move || match crate::xdg::get_app_info(info) { + Ok(info) => window.emit(&id, info), + Err(err) => window.emit( + &id, + Error { + error: err.to_string(), + }, + ), + }); + + Ok(cloned) +} + +#[cfg(target_os = "windows")] +#[tauri::command] +pub fn get_app_info<R: Runtime>( + window: Window<R>, + response_id: String, + _matching_path: String, + _exec_path: String, + _pid: i64, + _cmdline: String, +) -> Result { + let mut id = response_id; + + if id == "" { + id = uuid::Uuid::new_v4().to_string() + } + let cloned = id.clone(); + + std::thread::spawn(move || { + let _ = window.emit( + &id, + Error { + error: "Unsupported OS".to_string(), + }, + ); + }); + + Ok(cloned) +} + +#[tauri::command] +pub fn get_service_manager_status<R: Runtime>(window: Window<R>, response_id: String) -> Result { + let mut id = response_id; + + if id == "" { + id = uuid::Uuid::new_v4().to_string(); + } + let cloned = id.clone(); + + std::thread::spawn(move || { + let result = match get_service_manager() { + Ok(sm) => sm.status().map_err(|err| err.to_string()), + Err(err) => Err(err.to_string()), + }; + + match result { + Ok(result) => window.emit(&id, &result), + Err(err) => window.emit(&id, Error { error: err }), + } + }); + + Ok(cloned) +} + +#[tauri::command] +pub fn start_service<R: Runtime>(window: Window<R>, response_id: String) -> Result { + let mut id = response_id; + + if id == "" { + id = uuid::Uuid::new_v4().to_string(); + } + let cloned = id.clone(); + + std::thread::spawn(move || { + let result = match get_service_manager() { + Ok(sm) => sm.start().map_err(|err| err.to_string()), + Err(err) => Err(err.to_string()), + }; + + match result { + Ok(result) => window.emit(&id, &result), + Err(err) => window.emit(&id, Error { error: err }), + } + }); + + Ok(cloned) +} diff --git a/desktop/tauri/src-tauri/src/portmaster/mod.rs b/desktop/tauri/src-tauri/src/portmaster/mod.rs new file mode 100644 index 00000000..a5844949 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portmaster/mod.rs @@ -0,0 +1,294 @@ +/// This module contains a custom tauri plugin that handles all communication +/// with the angular app loaded from the portmaster api. +/// +/// Using a custom-plugin for this has the advantage that all code that has +/// access to a tauri::Window or a tauri::AppHandle can get access to the +/// portmaster plugin using the Runtime/Manager extension by just calling +/// window.portmaster() or app_handle.portmaster(). +/// +/// Any portmaster related features (like changing a portmaster setting) should +/// live in this module. +/// +/// Code that handles windows should NOT live here but should rather be placed +/// in the crate root. +// The commands module contains tauri commands that are available to Javascript +// using the invoke() and our custom invokeAsync() command. +mod commands; + +// The websocket module spawns an async function on tauri's runtime that manages +// a persistent connection to the Portmaster websocket API and updates the tauri Portmaster +// Plugin instance. +mod websocket; + +// The notification module manages system notifications from portmaster. +mod notifications; + +use crate::portapi::{ + client::PortAPI, message::Payload, models::config::BooleanValue, types::Request, +}; +use std::{ + collections::HashMap, + sync::atomic::{AtomicBool, Ordering}, +}; + +use log::debug; +use serde; +use std::sync::Mutex; +use tauri::{ + plugin::{Builder, TauriPlugin}, + AppHandle, Manager, Runtime, +}; + +pub trait Handler { + fn on_connect(&mut self, cli: PortAPI) -> (); + fn on_disconnect(&mut self); +} + +pub struct PortmasterPlugin<R: Runtime> { + #[allow(dead_code)] + app: AppHandle<R>, + + // state allows the angular application to store arbitrary values in the + // tauri application memory using the get_state and set_state + // tauri::commands. + state: Mutex<HashMap<String, String>>, + + // an atomic boolean that indicates if we're currently connected to + // portmaster or not. + is_reachable: AtomicBool, + + // holds the portapi client if any. + api: Mutex<Option<PortAPI>>, + + // a vector of handlers that should be invoked on connect and disconnect of + // the portmaster API. + handlers: Mutex<Vec<Box<dyn Handler + Send>>>, + + // whether or not we should handle notifications here. + handle_notifications: AtomicBool, + + // whether or not we should handle prompts. + handle_prompts: AtomicBool, + + // whether or not the angular application should call window.show after it + // finished bootstrapping. + should_show_after_bootstrap: AtomicBool, +} + +impl<R: Runtime> PortmasterPlugin<R> { + /// Returns a state stored in the portmaster plugin. + pub fn get_state(&self, key: String) -> Option<String> { + let map = self.state.lock(); + + if let Ok(map) = map { + match map.get(&key) { + Some(value) => Some(value.clone()), + None => None, + } + } else { + None + } + } + + /// Adds a new state to the portmaster plugin. + pub fn set_state(&self, key: String, value: String) { + let map = self.state.lock(); + + if let Ok(mut map) = map { + map.insert(key, value); + } + } + + /// Reports wheter or not we're currently connected to the Portmaster API. + pub fn is_reachable(&self) -> bool { + self.is_reachable.load(Ordering::Relaxed) + } + + /// Registers a new connection handler that is called on connect + /// and disconnect of the Portmaster websocket API. + pub fn register_handler(&self, mut handler: impl Handler + Send + 'static) { + // register_handler can only be invoked after the plugin setup + // completed. in this case, the websocket thread is already spawned and + // we might already be connected or know that the connection failed. + // Call the respective handler method immediately now. + if let Some(api) = self.get_api() { + handler.on_connect(api); + } else { + handler.on_disconnect(); + } + + if let Ok(mut handlers) = self.handlers.lock() { + handlers.push(Box::new(handler)); + } + } + + /// Returns the current portapi client. + pub fn get_api(&self) -> Option<PortAPI> { + if let Ok(mut api) = self.api.lock() { + match &mut *api { + Some(api) => Some(api.clone()), + None => None, + } + } else { + None + } + } + + /// Feature functions (enable/disable certain features). + + /// Configures whether or not our tauri app should show system + /// notifications. This excludes connection prompts. Use + /// with_connection_prompts to enable handling of connection prompts. + pub fn with_notification_support(&self, enable: bool) { + self.handle_notifications.store(enable, Ordering::Relaxed); + + // kick of the notification handler if we are connected. + if enable { + self.start_notification_handler(); + } + } + + /// Configures whether or not our angular application should show connection + /// prompts via tauri. + pub fn with_connection_prompts(&self, enable: bool) { + self.handle_prompts.store(enable, Ordering::Relaxed); + } + + /// Whether or not the angular application should call window.show after it + /// finished bootstrapping. + pub fn set_show_after_bootstrap(&self, show: bool) { + self.should_show_after_bootstrap + .store(show, Ordering::Relaxed); + } + + /// Returns whether or not the angular application should call window.show + /// after it finished bootstrapping. + pub fn get_show_after_bootstrap(&self) -> bool { + self.should_show_after_bootstrap.load(Ordering::Relaxed) + } + + /// Tells the angular applicatoin to show the window by emitting an event. + /// It calls set_show_after_bootstrap(true) automatically so the application + /// also shows after bootstrapping. + pub fn show_window(&self) { + debug!("[tauri] showing main window"); + + // set show_after_bootstrap to true so the app will even show if it + // misses the event below because it's still bootstrapping. + self.set_show_after_bootstrap(true); + + // ignore the error here, there's nothing we could do about it anyways. + let _ = self.app.emit("portmaster:show", ""); + } + + /// Enables or disables the SPN. + pub fn set_spn_enabled(&self, enabled: bool) { + if let Some(api) = self.get_api() { + let body: Result<Payload, serde_json::Error> = BooleanValue { + value: Some(enabled), + } + .try_into(); + + if let Ok(payload) = body { + tauri::async_runtime::spawn(async move { + _ = api + .request(Request::Update("config:spn/enable".to_string(), payload)) + .await; + }); + } + } + } + + //// Internal functions + fn start_notification_handler(&self) { + if let Some(api) = self.get_api() { + let cli = api.clone(); + tauri::async_runtime::spawn(async move { + notifications::notification_handler(cli).await; + }); + } + } + + /// Internal method to call all on_connect handlers + fn on_connect(&self, api: PortAPI) { + self.is_reachable.store(true, Ordering::Relaxed); + + // store the new api client. + let mut guard = self.api.lock().unwrap(); + *guard = Some(api.clone()); + drop(guard); + + // fire-off the notification handler. + if self.handle_notifications.load(Ordering::Relaxed) { + self.start_notification_handler(); + } + + if let Ok(mut handlers) = self.handlers.lock() { + for handler in handlers.iter_mut() { + handler.on_connect(api.clone()); + } + } + } + + /// Internal method to call all on_disconnect handlers + fn on_disconnect(&self) { + self.is_reachable.store(false, Ordering::Relaxed); + + // clear the current api client reference. + let mut guard = self.api.lock().unwrap(); + *guard = None; + drop(guard); + + if let Ok(mut handlers) = self.handlers.lock() { + for handler in handlers.iter_mut() { + handler.on_disconnect(); + } + } + } +} + +pub trait PortmasterExt<R: Runtime> { + fn portmaster(&self) -> &PortmasterPlugin<R>; +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Config {} + +impl<R: Runtime, T: Manager<R>> PortmasterExt<R> for T { + fn portmaster(&self) -> &PortmasterPlugin<R> { + self.state::<PortmasterPlugin<R>>().inner() + } +} + +pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> { + Builder::<R, Option<Config>>::new("portmaster") + .invoke_handler(tauri::generate_handler![ + commands::get_app_info, + commands::get_service_manager_status, + commands::start_service, + commands::get_state, + commands::set_state, + commands::should_show, + commands::should_handle_prompts + ]) + .setup(|app, _api| { + let plugin = PortmasterPlugin { + app: app.clone(), + state: Mutex::new(HashMap::new()), + is_reachable: AtomicBool::new(false), + handlers: Mutex::new(Vec::new()), + api: Mutex::new(None), + handle_notifications: AtomicBool::new(false), + handle_prompts: AtomicBool::new(false), + should_show_after_bootstrap: AtomicBool::new(true), + }; + + app.manage(plugin); + + // fire of the websocket handler + websocket::start_websocket_thread(app.clone()); + + Ok(()) + }) + .build() +} diff --git a/desktop/tauri/src-tauri/src/portmaster/notifications.rs b/desktop/tauri/src-tauri/src/portmaster/notifications.rs new file mode 100644 index 00000000..88472639 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portmaster/notifications.rs @@ -0,0 +1,103 @@ +use crate::portapi::client::*; +use crate::portapi::message::*; +use crate::portapi::models::notification::*; +use crate::portapi::types::*; +use log::error; +use notify_rust; +use serde_json::json; +#[allow(unused_imports)] +use tauri::async_runtime; + +pub async fn notification_handler(cli: PortAPI) { + let res = cli + .request(Request::QuerySubscribe("query notifications:".to_string())) + .await; + + if let Ok(mut rx) = res { + while let Some(msg) = rx.recv().await { + let res = match msg { + Response::Ok(key, payload) => Some((key, payload)), + Response::New(key, payload) => Some((key, payload)), + Response::Update(key, payload) => Some((key, payload)), + _ => None, + }; + + if let Some((key, payload)) = res { + match payload.parse::<Notification>() { + Ok(n) => { + // Skip if this one should not be shown using the system notifications + if !n.show_on_system { + return; + } + + // Skip if this action has already been acted on + if n.selected_action_id != "" { + return; + } + + // TODO(ppacher): keep a reference of open notifications and close them + // if the user reacted inside the UI: + + let mut notif = notify_rust::Notification::new(); + notif.body(&n.message); + notif.timeout(notify_rust::Timeout::Never); // TODO(ppacher): use n.expires to calculate the timeout. + notif.summary(&n.title); + notif.icon("portmaster"); + + for action in n.actions { + notif.action(&action.id, &action.text); + } + + #[cfg(target_os = "linux")] + { + let cli_clone = cli.clone(); + async_runtime::spawn(async move { + let res = notif.show(); + match res { + Ok(handle) => { + handle.wait_for_action(|action| { + match action { + "__closed" => { + // timeout + } + + value => { + let value = value.to_string().clone(); + + async_runtime::spawn(async move { + let _ = cli_clone + .request(Request::Update( + key, + Payload::JSON( + json!({ + "SelectedActionID": value + }) + .to_string(), + ), + )) + .await; + }); + } + } + }) + } + Err(err) => { + error!("failed to display notification: {}", err); + } + } + }); + } + } + Err(err) => match err { + ParseError::JSON(err) => { + error!("failed to parse notification: {}", err); + } + _ => { + error!("unknown error when parsing notifications payload"); + } + }, + } + } + } + } +} diff --git a/desktop/tauri/src-tauri/src/portmaster/websocket.rs b/desktop/tauri/src-tauri/src/portmaster/websocket.rs new file mode 100644 index 00000000..6dca8519 --- /dev/null +++ b/desktop/tauri/src-tauri/src/portmaster/websocket.rs @@ -0,0 +1,45 @@ +use super::PortmasterExt; +use crate::portapi::client::connect; +use log::{debug, error, info, warn}; +use tauri::{AppHandle, Runtime}; +use tokio::time::{sleep, Duration}; + +/// Starts a backround thread (via tauri::async_runtime) that connects to the Portmaster +/// Websocket database API. +pub fn start_websocket_thread<R: Runtime>(app: AppHandle<R>) { + let app = app.clone(); + + tauri::async_runtime::spawn(async move { + loop { + debug!("Trying to connect to websocket endpoint"); + + let api = connect("ws://127.0.0.1:817/api/database/v1").await; + + match api { + Ok(cli) => { + let portmaster = app.portmaster(); + + info!("Successfully connected to portmaster"); + + portmaster.on_connect(cli.clone()); + + while !cli.is_closed() { + let _ = sleep(Duration::from_secs(1)).await; + } + + portmaster.on_disconnect(); + + warn!("lost connection to portmaster, retrying ....") + } + Err(err) => { + error!("failed to create portapi client: {}", err); + + app.portmaster().on_disconnect(); + + // sleep and retry + sleep(Duration::from_secs(2)).await; + } + } + } + }); +} diff --git a/desktop/tauri/src-tauri/src/service/manager.rs b/desktop/tauri/src-tauri/src/service/manager.rs new file mode 100644 index 00000000..7a79e061 --- /dev/null +++ b/desktop/tauri/src-tauri/src/service/manager.rs @@ -0,0 +1,17 @@ +use std::process::{Command, ExitStatus, Stdio}; +use std::{fs, io}; + +use thiserror::Error; + +#[cfg(target_os = "linux")] +use std::os::unix::fs::PermissionsExt; + +use super::status::StatusResult; + +static SYSTEMCTL: &str = "systemctl"; +// TODO(ppacher): add support for kdesudo and gksudo + +enum SudoCommand { + Pkexec, + Gksu, +} diff --git a/desktop/tauri/src-tauri/src/service/mod.rs b/desktop/tauri/src-tauri/src/service/mod.rs new file mode 100644 index 00000000..ac55a39f --- /dev/null +++ b/desktop/tauri/src-tauri/src/service/mod.rs @@ -0,0 +1,76 @@ +// pub mod manager; +pub mod status; + +#[cfg(target_os = "linux")] +mod systemd; + +#[cfg(target_os = "windows")] +mod windows_service; + +use std::process::ExitStatus; + +#[cfg(target_os = "linux")] +use crate::service::systemd::SystemdServiceManager; + +use log::info; +use thiserror::Error; + +use self::status::StatusResult; + +#[allow(dead_code)] +#[derive(Error, Debug)] +pub enum ServiceManagerError { + #[error("unsupported service manager")] + UnsupportedServiceManager, + + #[error("unsupported operating system")] + UnsupportedOperatingSystem, + + #[error(transparent)] + FromUtf8Error(#[from] std::string::FromUtf8Error), + + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error("{0} output={1}")] + Other(ExitStatus, String), + + #[error("{0}")] + WindowsError(String), +} + +pub type Result<T> = std::result::Result<T, ServiceManagerError>; + +/// A common interface to the system manager service (might be systemd, openrc, sc.exe, ...) +pub trait ServiceManager { + fn status(&self) -> Result<StatusResult>; + fn start(&self) -> Result<StatusResult>; +} + +struct EmptyServiceManager(); + +impl ServiceManager for EmptyServiceManager { + fn status(&self) -> Result<StatusResult> { + Err(ServiceManagerError::UnsupportedServiceManager) + } + + fn start(&self) -> Result<StatusResult> { + Err(ServiceManagerError::UnsupportedServiceManager) + } +} + +pub fn get_service_manager() -> Result<impl ServiceManager> { + #[cfg(target_os = "linux")] + { + if SystemdServiceManager::is_installed() { + info!("system service manager: systemd"); + + Ok(SystemdServiceManager {}) + } else { + Err(ServiceManagerError::UnsupportedServiceManager) + } + } + + #[cfg(target_os = "windows")] + return Ok(windows_service::SERVICE_MANGER.clone()); +} diff --git a/desktop/tauri/src-tauri/src/service/status.rs b/desktop/tauri/src-tauri/src/service/status.rs new file mode 100644 index 00000000..30f4841d --- /dev/null +++ b/desktop/tauri/src-tauri/src/service/status.rs @@ -0,0 +1,27 @@ +use serde::{Serialize, Deserialize}; + +/// SystemResult defines the "success" codes when querying or starting +/// a system service. +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum StatusResult { + // The requested system service is installed and currently running. + Running, + + // The requested system service is installed but currently stopped. + Stopped, + + // NotFound is returned when the system service (systemd unit for linux) + // has not been found and the system and likely means the Portmaster installtion + // is broken all together. + NotFound, +} + +impl std::fmt::Display for StatusResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StatusResult::Running => write!(f, "running"), + StatusResult::Stopped => write!(f, "stopped"), + StatusResult::NotFound => write!(f, "not installed") + } + } +} diff --git a/desktop/tauri/src-tauri/src/service/systemd.rs b/desktop/tauri/src-tauri/src/service/systemd.rs new file mode 100644 index 00000000..770a9139 --- /dev/null +++ b/desktop/tauri/src-tauri/src/service/systemd.rs @@ -0,0 +1,246 @@ +use log::{debug, error}; + +use super::status::StatusResult; +use super::{Result, ServiceManager, ServiceManagerError}; +use std::os::unix::fs::PermissionsExt; +use std::{ + fs, io, + process::{Command, ExitStatus, Stdio}, +}; + +static SYSTEMCTL: &str = "systemctl"; +// TODO(ppacher): add support for kdesudo and gksudo + +enum SudoCommand { + Pkexec, + Gksu, +} + +impl From<std::process::Output> for ServiceManagerError { + fn from(output: std::process::Output) -> Self { + let msg = String::from_utf8(output.stderr) + .ok() + .filter(|s| !s.trim().is_empty()) + .or_else(|| { + String::from_utf8(output.stdout) + .ok() + .filter(|s| !s.trim().is_empty()) + }) + .unwrap_or_else(|| format!("Failed to run `systemctl`")); + + ServiceManagerError::Other(output.status, msg) + } +} + +/// System Service manager implementation for Linux based distros. +pub struct SystemdServiceManager {} + +impl SystemdServiceManager { + /// Checks if systemctl is available in /sbin/ /bin, /usr/bin or /usr/sbin. + /// + /// Note that we explicitly check those paths to avoid returning true in case + /// there's a systemctl binary in the cwd and PATH includes . since this may + /// pose a security risk of running an untrusted binary with root privileges. + pub fn is_installed() -> bool { + let paths = vec![ + "/sbin/systemctl", + "/bin/systemctl", + "/usr/sbin/systemctl", + "/usr/bin/systemctl", + ]; + + for path in paths { + debug!("checking for systemctl at path {}", path); + + match fs::metadata(path) { + Ok(md) => { + debug!("found systemctl at path {} ", path); + + if md.is_file() && md.permissions().mode() & 0o111 != 0 { + return true; + } + + error!( + "systemctl binary found but invalid permissions: {}", + md.permissions().mode().to_string() + ); + } + Err(err) => { + error!( + "failed to check systemctl binary at {}: {}", + path, + err.to_string() + ); + + continue; + } + }; + } + + error!("failed to find systemctl binary"); + + false + } +} + +impl ServiceManager for SystemdServiceManager { + fn status(&self) -> super::Result<StatusResult> { + let name = "portmaster.service"; + let result = systemctl("is-active", name, false); + + match result { + // If `systemctl is-active` returns without an error code and stdout matches "active" (just to guard againt + // unhandled cases), the service can be considered running. + Ok(stdout) => { + let mut copy = stdout.to_owned(); + trim_newline(&mut copy); + + if copy != "active" { + // make sure the output is as we expected + Err(ServiceManagerError::Other(ExitStatus::default(), stdout)) + } else { + Ok(StatusResult::Running) + } + } + + Err(e) => { + if let ServiceManagerError::Other(_err, ref output) = e { + let mut copy = output.to_owned(); + trim_newline(&mut copy); + + if copy == "inactive" { + return Ok(StatusResult::Stopped); + } + } else { + error!("failed to run 'systemctl is-active': {}", e.to_string()); + } + + // Failed to check if the unit is running + match systemctl("cat", name, false) { + // "systemctl cat" seems to no have stable exit codes so we need + // to check the output if it looks like "No files found for yyyy.service" + // At least, the exit code are not documented for systemd v255 (newest at the time of writing) + Err(ServiceManagerError::Other(status, msg)) => { + if msg.contains("No files found for") { + Ok(StatusResult::NotFound) + } else { + Err(ServiceManagerError::Other(status, msg)) + } + } + + // Any other error type means something went completely wrong while running systemctl altogether. + Err(e) => Err(e), + + // Fine, systemctl cat worked so if the output is "inactive" we know the service is installed + // but stopped. + Ok(_) => { + // Unit seems to be installed so check the output of result + let mut stderr = e.to_string(); + trim_newline(&mut stderr); + + if stderr == "inactive" { + Ok(StatusResult::Stopped) + } else { + Err(e) + } + } + } + } + } + } + + fn start(&self) -> Result<StatusResult> { + let name = "portmaster.service"; + + // This time we need to run as root through pkexec or similar binaries like kdesudo/gksudo. + systemctl("start", name, true)?; + + // Check the status again to be sure it's started now + self.status() + } +} + +fn systemctl( + cmd: &str, + unit: &str, + run_as_root: bool, +) -> std::result::Result<String, ServiceManagerError> { + let output = run(run_as_root, SYSTEMCTL, vec![cmd, unit])?; + + // The command have been able to run (i.e. has been spawned and executed by the kernel). + // We now need to check the exit code and "stdout/stderr" output in case of an error. + if output.status.success() { + Ok(String::from_utf8(output.stdout)?) + } else { + Err(output.into()) + } +} + +fn run<'a>(root: bool, cmd: &'a str, args: Vec<&'a str>) -> std::io::Result<std::process::Output> { + // clone the args vector so we can insert the actual command in case we're running + // through pkexec or friends. This is just callled a couple of times on start-up + // so cloning the vector does not add any mentionable performance impact here and it's better + // than expecting a mutalble vector in the first place. + + let mut args = args.to_vec(); + + let mut command = match root { + true => { + // if we run through pkexec and friends we need to append cmd as the second argument. + + args.insert(0, cmd); + match get_sudo_cmd() { + Ok(cmd) => { + match cmd { + SudoCommand::Pkexec => { + // disable the internal text-based prompt agent from pkexec because it won't work anyway. + args.insert(0, "--disable-internal-agent"); + Command::new("/usr/bin/pkexec") + } + SudoCommand::Gksu => { + args.insert(0, "--message=Please enter your password:"); + args.insert(1, "--sudo-mode"); + + Command::new("/usr/bin/gksudo") + } + } + } + Err(err) => return Err(err), + } + } + false => Command::new(cmd), + }; + + command.env("LC_ALL", "C"); + + command + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + command.args(args).output() +} + +fn trim_newline(s: &mut String) { + if s.ends_with('\n') { + s.pop(); + if s.ends_with('\r') { + s.pop(); + } + } +} + +fn get_sudo_cmd() -> std::result::Result<SudoCommand, std::io::Error> { + if let Ok(_) = fs::metadata("/usr/bin/pkexec") { + return Ok(SudoCommand::Pkexec); + } + + if let Ok(_) = fs::metadata("/usr/bin/gksudo") { + return Ok(SudoCommand::Gksu); + } + + Err(std::io::Error::new( + io::ErrorKind::NotFound, + "failed to detect sudo command", + )) +} diff --git a/desktop/tauri/src-tauri/src/service/windows_service.rs b/desktop/tauri/src-tauri/src/service/windows_service.rs new file mode 100644 index 00000000..2146cc41 --- /dev/null +++ b/desktop/tauri/src-tauri/src/service/windows_service.rs @@ -0,0 +1,167 @@ +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; + +use windows::{ + core::{HSTRING, PCWSTR}, + Win32::{Foundation::HWND, UI::WindowsAndMessaging::SHOW_WINDOW_CMD}, +}; +use windows_service::{ + service::{Service, ServiceAccess}, + service_manager::{ServiceManager, ServiceManagerAccess}, +}; + +const SERVICE_NAME: &str = "PortmasterCore"; + +pub struct WindowsServiceManager { + manager: Option<ServiceManager>, + service: Option<Service>, +} + +lazy_static! { + pub static ref SERVICE_MANGER: Arc<Mutex<WindowsServiceManager>> = + Arc::new(Mutex::new(WindowsServiceManager::new())); +} + +impl WindowsServiceManager { + pub fn new() -> Self { + Self { + manager: None, + service: None, + } + } + + fn init_manager(&mut self) -> super::Result<()> { + // Initialize service manager. This connects to the active service database and can query status. + let manager = match ServiceManager::local_computer( + None::<&str>, + ServiceManagerAccess::ENUMERATE_SERVICE, // Only query status is allowed form non privileged application. + ) { + Ok(manager) => manager, + Err(err) => { + return Err(windows_to_manager_err(err)); + } + }; + self.manager = Some(manager); + Ok(()) + } + + fn open_service(&mut self) -> super::Result<bool> { + if let None = self.manager { + self.init_manager()?; + } + + if let Some(manager) = &self.manager { + let service = match manager.open_service(SERVICE_NAME, ServiceAccess::QUERY_STATUS) { + Ok(service) => service, + Err(_) => { + return Ok(false); // Service is not installed. + } + }; + // Service is installed and the state can be queried. + self.service = Some(service); + return Ok(true); + } + + return Err(super::ServiceManagerError::WindowsError( + "failed to initialize manager".to_string(), + )); + } +} + +impl super::ServiceManager for Arc<Mutex<WindowsServiceManager>> { + fn status(&self) -> super::Result<super::status::StatusResult> { + if let Ok(mut manager) = self.lock() { + if let None = manager.service { + // Try to open service + if !manager.open_service()? { + // Service is not installed. + return Ok(super::status::StatusResult::NotFound); + } + } + + if let Some(service) = &manager.service { + match service.query_status() { + Ok(status) => match status.current_state { + windows_service::service::ServiceState::Stopped + | windows_service::service::ServiceState::StopPending + | windows_service::service::ServiceState::PausePending + | windows_service::service::ServiceState::StartPending + | windows_service::service::ServiceState::ContinuePending + | windows_service::service::ServiceState::Paused => { + // Stopped or in a transition state. + return Ok(super::status::StatusResult::Stopped); + } + windows_service::service::ServiceState::Running => { + // Everything expect Running state is considered stopped. + return Ok(super::status::StatusResult::Running); + } + }, + Err(err) => { + return Err(super::ServiceManagerError::WindowsError(err.to_string())); + } + } + } + } + // This should be unreachable. + Ok(super::status::StatusResult::NotFound) + } + + fn start(&self) -> super::Result<super::status::StatusResult> { + if let Ok(mut service_manager) = self.lock() { + // Check if service is installed. + if let None = &service_manager.service { + if let Err(_) = service_manager.open_service() { + return Ok(super::status::StatusResult::NotFound); + } + } + + // Run service manager with elevated privileges. This will show access popup. + unsafe { + windows::Win32::UI::Shell::ShellExecuteW( + HWND::default(), + &HSTRING::from("runas"), + &HSTRING::from("C:\\Windows\\System32\\sc.exe"), + &HSTRING::from(format!("start {}", SERVICE_NAME)), + PCWSTR::null(), + SHOW_WINDOW_CMD(0), + ); + } + + // Wait for service to start. Timeout 10s (100 * 100ms). + if let Some(service) = &service_manager.service { + for _ in 0..100 { + match service.query_status() { + Ok(status) => { + if let windows_service::service::ServiceState::Running = + status.current_state + { + return Ok(super::status::StatusResult::Running); + } else { + std::thread::sleep(Duration::from_millis(100)); + } + } + Err(err) => return Err(windows_to_manager_err(err)), + } + } + } + // Timeout starting the service. + return Ok(super::status::StatusResult::Stopped); + } + return Err(super::ServiceManagerError::WindowsError( + "failed to start service".to_string(), + )); + } +} + +fn windows_to_manager_err(err: windows_service::Error) -> super::ServiceManagerError { + if let windows_service::Error::Winapi(_) = err { + // Winapi does not contain the full error. Get the actual error from windows. + return super::ServiceManagerError::WindowsError( + windows::core::Error::from_win32().to_string(), // Internally will call `GetLastError()` and parse the result. + ); + } else { + return super::ServiceManagerError::WindowsError(err.to_string()); + } +} diff --git a/desktop/tauri/src-tauri/src/traymenu.rs b/desktop/tauri/src-tauri/src/traymenu.rs new file mode 100644 index 00000000..6456797f --- /dev/null +++ b/desktop/tauri/src-tauri/src/traymenu.rs @@ -0,0 +1,344 @@ +use std::collections::HashMap; +use std::sync::Mutex; + +use log::{debug, error}; +use tauri::{ + menu::{ + CheckMenuItem, CheckMenuItemBuilder, MenuBuilder, MenuItemBuilder, PredefinedMenuItem, + SubmenuBuilder, + }, + tray::{ClickType, TrayIcon, TrayIconBuilder}, + Icon, Manager, Wry, +}; +use tauri_plugin_dialog::DialogExt; + +use crate::{ + portapi::{ + client::PortAPI, + message::ParseError, + models::{ + config::BooleanValue, + spn::SPNStatus, + subsystem::{self, Subsystem}, + }, + types::{Request, Response}, + }, + portmaster::PortmasterExt, + window::{create_main_window, may_navigate_to_ui, open_window}, +}; + +pub type AppIcon = TrayIcon<Wry>; + +lazy_static! { + // Set once setup_tray_menu executed. + static ref SPN_BUTTON: Mutex<Option<CheckMenuItem<Wry>>> = Mutex::new(None); +} + +// Icons +// +const BLUE_ICON: &'static [u8] = + include_bytes!("../../assets/icons/pm_light_blue_512.ico"); +const RED_ICON: &'static [u8] = + include_bytes!("../../assets/icons/pm_light_red_512.ico"); +const YELLOW_ICON: &'static [u8] = + include_bytes!("../../assets/icons/pm_light_yellow_512.ico"); +const GREEN_ICON: &'static [u8] = + include_bytes!("../../assets/icons/pm_light_green_512.ico"); + +pub fn setup_tray_menu( + app: &mut tauri::App, +) -> core::result::Result<AppIcon, Box<dyn std::error::Error>> { + // Tray menu + let close_btn = MenuItemBuilder::with_id("close", "Exit").build(app); + let open_btn = MenuItemBuilder::with_id("open", "Open").build(app); + + let spn = CheckMenuItemBuilder::with_id("spn", "Use SPN").build(app); + + // Store the SPN button reference + let mut button_ref = SPN_BUTTON.lock().unwrap(); + *button_ref = Some(spn.clone()); + + let force_show_window = MenuItemBuilder::with_id("force-show", "Force Show UI").build(app); + let reload_btn = MenuItemBuilder::with_id("reload", "Reload User Interface").build(app); + let developer_menu = SubmenuBuilder::new(app, "Developer") + .items(&[&reload_btn, &force_show_window]) + .build()?; + + // Drop the reference now so we unlock immediately. + drop(button_ref); + + let menu = MenuBuilder::new(app) + .items(&[ + &spn, + &PredefinedMenuItem::separator(app), + &open_btn, + &close_btn, + &developer_menu, + ]) + .build()?; + + let icon = TrayIconBuilder::new() + .icon(Icon::Raw(RED_ICON.to_vec())) + .menu(&menu) + .on_menu_event(move |app, event| match event.id().as_ref() { + "close" => { + let handle = app.clone(); + app.dialog() + .message("This does not stop the Portmaster system service") + .title("Do you really want to quit the user interface?") + .ok_button_label("Yes, exit") + .cancel_button_label("No") + .show(move |answer| { + if answer { + let _ = handle.emit("exit-requested", ""); + handle.exit(0); + } + }); + } + "open" => { + let _ = open_window(app); + } + "reload" => { + if let Ok(mut win) = open_window(app) { + may_navigate_to_ui(&mut win, true); + } + } + "force-show" => { + match create_main_window(app) { + Ok(mut win) => { + may_navigate_to_ui(&mut win, true); + if let Err(err) = win.show() { + error!("[tauri] failed to show window: {}", err.to_string()); + }; + } + Err(err) => { + error!("[tauri] failed to create main window: {}", err.to_string()); + } + }; + } + "spn" => { + let btn = SPN_BUTTON.lock().unwrap(); + + if let Some(bt) = &*btn { + if let Ok(is_checked) = bt.is_checked() { + app.portmaster().set_spn_enabled(is_checked); + } + } + } + other => { + error!("unknown menu event id: {}", other); + } + }) + .on_tray_icon_event(|tray, event| { + // not supported on linux + if event.click_type == ClickType::Left { + let _ = open_window(tray.app_handle()); + } + }) + .build(app)?; + + Ok(icon) +} + +pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_status: String) { + // iterate over the subsytems and check if there's a module failure + let failure = subsystems + .values() + .into_iter() + .map(|s| s.failure_status) + .fold( + subsystem::FAILURE_NONE, + |acc, s| { + if s > acc { + s + } else { + acc + } + }, + ); + + let next_icon = match failure { + subsystem::FAILURE_WARNING => YELLOW_ICON, + subsystem::FAILURE_ERROR => RED_ICON, + _ => match spn_status.as_str() { + "connected" | "connecting" => BLUE_ICON, + _ => GREEN_ICON, + }, + }; + + _ = icon.set_icon(Some(Icon::Raw(next_icon.to_vec()))); +} + +pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) { + let icon = match app.tray() { + Some(icon) => icon, + None => { + error!("cancel try_handler: missing try icon"); + return; + } + }; + + let mut subsystem_subscription = match cli + .request(Request::QuerySubscribe( + "query runtime:subsystems/".to_string(), + )) + .await + { + Ok(rx) => rx, + Err(err) => { + error!( + "cancel try_handler: failed to subscribe to 'runtime:subsystems': {}", + err + ); + return; + } + }; + + let mut spn_status_subscription = match cli + .request(Request::QuerySubscribe( + "query runtime:spn/status".to_string(), + )) + .await + { + Ok(rx) => rx, + Err(err) => { + error!( + "cancel try_handler: failed to subscribe to 'runtime:spn/status': {}", + err + ); + return; + } + }; + + let mut spn_config_subscription = match cli + .request(Request::QuerySubscribe( + "query config:spn/enable".to_string(), + )) + .await + { + Ok(rx) => rx, + Err(err) => { + error!( + "cancel try_handler: failed to subscribe to 'runtime:spn/enable': {}", + err + ); + return; + } + }; + + _ = icon.set_icon(Some(Icon::Raw(BLUE_ICON.to_vec()))); + + let mut subsystems: HashMap<String, Subsystem> = HashMap::new(); + let mut spn_status: String = "".to_string(); + + loop { + tokio::select! { + msg = subsystem_subscription.recv() => { + let msg = match msg { + Some(m) => m, + None => { break } + }; + + let res = match msg { + Response::Ok(key, payload) => Some((key, payload)), + Response::New(key, payload) => Some((key, payload)), + Response::Update(key, payload) => Some((key, payload)), + _ => None, + }; + + if let Some((_, payload)) = res { + match payload.parse::<Subsystem>() { + Ok(n) => { + subsystems.insert(n.id.clone(), n); + + update_icon(icon.clone(), subsystems.clone(), spn_status.clone()); + }, + Err(err) => match err { + ParseError::JSON(err) => { + error!("failed to parse subsystem: {}", err); + } + _ => { + error!("unknown error when parsing notifications payload"); + } + }, + } + } + }, + msg = spn_status_subscription.recv() => { + let msg = match msg { + Some(m) => m, + None => { break } + }; + + let res = match msg { + Response::Ok(key, payload) => Some((key, payload)), + Response::New(key, payload) => Some((key, payload)), + Response::Update(key, payload) => Some((key, payload)), + _ => None, + }; + + if let Some((_, payload)) = res { + match payload.parse::<SPNStatus>() { + Ok(value) => { + debug!("SPN status update: {}", value.status); + spn_status = value.status.clone(); + + update_icon(icon.clone(), subsystems.clone(), spn_status.clone()); + }, + Err(err) => match err { + ParseError::JSON(err) => { + error!("failed to parse spn status value: {}", err) + }, + _ => { + error!("unknown error when parsing spn status value") + } + } + } + } + }, + msg = spn_config_subscription.recv() => { + let msg = match msg { + Some(m) => m, + None => { break } + }; + + let res = match msg { + Response::Ok(key, payload) => Some((key, payload)), + Response::New(key, payload) => Some((key, payload)), + Response::Update(key, payload) => Some((key, payload)), + _ => None, + }; + + if let Some((_, payload)) = res { + match payload.parse::<BooleanValue>() { + Ok(value) => { + let mut btn = SPN_BUTTON.lock().unwrap(); + + if let Some(btn) = &mut *btn { + if let Some(value) = value.value { + _ = btn.set_checked(value); + } else { + _ = btn.set_checked(false); + } + } + }, + Err(err) => match err { + ParseError::JSON(err) => { + error!("failed to parse config value: {}", err) + }, + _ => { + error!("unknown error when parsing config value") + } + } + } + } + } + } + } + + if let Some(btn) = &mut *(SPN_BUTTON.lock().unwrap()) { + _ = btn.set_checked(false); + } + + _ = icon.set_icon(Some(Icon::Raw(RED_ICON.to_vec()))); +} diff --git a/desktop/tauri/src-tauri/src/window.rs b/desktop/tauri/src-tauri/src/window.rs new file mode 100644 index 00000000..2059a134 --- /dev/null +++ b/desktop/tauri/src-tauri/src/window.rs @@ -0,0 +1,151 @@ +use log::{debug, error}; +use tauri::{AppHandle, Manager, Result, UserAttentionType, Window, WindowBuilder, WindowUrl}; + +use crate::portmaster::PortmasterExt; + +/// Either returns the existing "main" window or creates a new one. +/// +/// The window is not automatically shown (i.e it starts hidden). +/// If a new main window is created (i.e. the tauri app was minimized to system-tray) +/// then the window will be automatically navigated to the Portmaster UI endpoint +/// if ::websocket::is_portapi_reachable returns true. +/// +/// Either the existing or the newly created window is returned. +pub fn create_main_window(app: &AppHandle) -> Result<Window> { + let mut window = if let Some(window) = app.get_window("main") { + debug!("[tauri] main window already created"); + + window + } else { + debug!("[tauri] creating main window"); + + let res = WindowBuilder::new(app, "main", WindowUrl::App("index.html".into())) + .visible(false) + .build(); + + match res { + Ok(win) => { + win.once("tauri://error", |event| { + error!("failed to open tauri window: {}", event.payload()); + }); + + win + } + Err(err) => { + error!("[tauri] failed to create main window: {}", err.to_string()); + + return Err(err); + } + } + }; + + // If the window is not yet navigated to the Portmaster UI, do it now. + may_navigate_to_ui(&mut window, false); + + #[cfg(debug_assertions)] + if let Ok(_) = std::env::var("TAURI_SHOW_IMMEDIATELY") { + debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window"); + + if let Err(err) = window.show() { + error!("[tauri] failed to show window: {}", err.to_string()); + } + } + + Ok(window) +} + +pub fn create_splash_window(app: &AppHandle) -> Result<Window> { + if let Some(window) = app.get_window("splash") { + let _ = window.show(); + Ok(window) + } else { + let window = WindowBuilder::new(app, "splash", WindowUrl::App("index.html".into())) + .center() + .closable(false) + .focused(true) + .resizable(false) + .visible(true) + .title("Portmaster") + .inner_size(600.0, 250.0) + .build()?; + + let _ = window.request_user_attention(Some(UserAttentionType::Informational)); + + Ok(window) + } +} + +pub fn close_splash_window(app: &AppHandle) -> Result<()> { + if let Some(window) = app.get_window("splash") { + return window.close(); + } + return Err(tauri::Error::WindowNotFound); +} + +/// Opens a window for the tauri application. +/// +/// If the main window has already been created, it is instructed to +/// show even if we're currently not connected to Portmaster. +/// This is safe since the main-window will only be created if Portmaster API +/// was reachable so the angular application must have finished bootstrapping. +/// +/// If there's not main window and the Portmaster API is reachable we create a new +/// main window. +/// +/// If the Portmaster API is unreachable and there's no main window yet, we show the +/// splash-screen window. +pub fn open_window(app: &AppHandle) -> Result<Window> { + if app.portmaster().is_reachable() { + match app.get_window("main") { + Some(win) => { + app.portmaster().show_window(); + + Ok(win) + } + None => { + app.portmaster().show_window(); + + create_main_window(app) + } + } + } else { + debug!("Show splash screen"); + create_splash_window(app) + } +} + +/// If the Portmaster Websocket database API is reachable the window will be navigated +/// to the HTTP endpoint of Portmaster to load the UI from there. +/// +/// Note that only happens if the window URL does not already point to the PM API. +/// +/// In #[cfg(debug_assertions)] the TAURI_PM_URL environment variable will be used +/// if set. +/// Otherwise or in release builds, it will be navigated to http://127.0.0.1:817. +pub fn may_navigate_to_ui(win: &mut Window, force: bool) { + if !win.app_handle().portmaster().is_reachable() && !force { + error!("[tauri] portmaster API is not reachable, not navigating"); + + return; + } + + if force || cfg!(debug_assertions) || win.url().host_str() != Some("localhost") { + #[cfg(debug_assertions)] + if let Ok(target_url) = std::env::var("TAURI_PM_URL") { + debug!("[tauri] navigating to {}", target_url); + + win.navigate(target_url.parse().unwrap()); + + return; + } + + #[cfg(debug_assertions)] + { + debug!("[tauri] navigating to http://localhost:4200"); + win.navigate("http://localhost:4200".parse().unwrap()); + } + + #[cfg(not(debug_assertions))] + win.navigate("http://localhost:817".parse().unwrap()); + } +} diff --git a/desktop/tauri/src-tauri/src/xdg/mod.rs b/desktop/tauri/src-tauri/src/xdg/mod.rs new file mode 100644 index 00000000..b1fa9089 --- /dev/null +++ b/desktop/tauri/src-tauri/src/xdg/mod.rs @@ -0,0 +1,585 @@ +use cached::proc_macro::once; +use dataurl::DataUrl; +use gdk_pixbuf::{Pixbuf, PixbufError}; +use gtk_sys::{ + gtk_icon_info_free, gtk_icon_info_get_filename, gtk_icon_theme_get_default, + gtk_icon_theme_lookup_icon, GtkIconTheme, +}; +use log::{debug, error}; +use std::collections::HashMap; +use std::ffi::c_int; +use std::ffi::{CStr, CString}; +use std::io; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, RwLock}; +use std::{ + env, fs, + io::{Error, ErrorKind}, +}; +use thiserror::Error; + +use dirs; +use ini::{Ini, ParseOption}; + +static mut GTK_DEFAULT_THEME: Option<*mut GtkIconTheme> = None; + +lazy_static! { + static ref APP_INFO_CACHE: Arc<RwLock<HashMap<String, Option<AppInfo>>>> = + Arc::new(RwLock::new(HashMap::new())); +} + +#[derive(Debug, Error)] +pub enum LookupError { + #[error(transparent)] + IoError(#[from] std::io::Error), +} + +pub type Result<T> = std::result::Result<T, LookupError>; + +#[derive(Clone, serde::Serialize)] +pub struct AppInfo { + pub icon_name: String, + pub app_name: String, + pub icon_dataurl: String, + pub comment: String, +} + +impl Default for AppInfo { + fn default() -> Self { + AppInfo { + icon_dataurl: "".to_string(), + icon_name: "".to_string(), + app_name: "".to_string(), + comment: "".to_string(), + } + } +} + +#[derive(Clone, serde::Serialize, Debug)] +pub struct ProcessInfo { + pub exec_path: String, + pub cmdline: String, + pub pid: i64, + pub matching_path: String, +} + +impl std::fmt::Display for ProcessInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} (cmdline={}) (pid={}) (matching_path={})", + self.exec_path, self.cmdline, self.pid, self.matching_path + ) + } +} + +pub fn get_app_info(process_info: ProcessInfo) -> Result<AppInfo> { + { + let cache = APP_INFO_CACHE.read().unwrap(); + + if let Some(value) = cache.get(process_info.exec_path.as_str()) { + match value { + Some(app_info) => return Ok(app_info.clone()), + None => { + return Err(LookupError::IoError(io::Error::new( + io::ErrorKind::NotFound, + "not found", + ))) + } + } + } + } + + let mut needles = Vec::new(); + if !process_info.exec_path.is_empty() { + needles.push(process_info.exec_path.as_str()) + } + if !process_info.cmdline.is_empty() { + needles.push(process_info.cmdline.as_str()) + } + if !process_info.matching_path.is_empty() { + needles.push(process_info.matching_path.as_str()) + } + + // sort and deduplicate + needles.sort(); + needles.dedup(); + + debug!("Searching app info for {:?}", process_info); + + let mut desktop_files = Vec::new(); + for dir in get_application_directories()? { + let mut files = find_desktop_files(dir.as_path())?; + desktop_files.append(&mut files); + } + + let mut matches = Vec::new(); + for needle in needles.clone() { + debug!("Trying needle {} on exec path", needle); + + match try_get_app_info(needle, CheckType::Exec, &desktop_files) { + Ok(mut result) => { + matches.append(&mut result); + } + Err(LookupError::IoError(ioerr)) => { + if ioerr.kind() != ErrorKind::NotFound { + return Err(ioerr.into()); + } + } + }; + + match try_get_app_info(needle, CheckType::Name, &desktop_files) { + Ok(mut result) => { + matches.append(&mut result); + } + Err(LookupError::IoError(ioerr)) => { + if ioerr.kind() != ErrorKind::NotFound { + return Err(ioerr.into()); + } + } + }; + } + + if matches.is_empty() { + APP_INFO_CACHE + .write() + .unwrap() + .insert(process_info.exec_path, None); + + Err(Error::new(ErrorKind::NotFound, format!("failed to find app info")).into()) + } else { + // sort matches by length + matches.sort_by(|a, b| a.1.cmp(&b.1)); + + for mut info in matches { + match get_icon_as_png_dataurl(&info.0.icon_name, 32) { + Ok(du) => { + debug!( + "[xdg] best match for {:?} is {:?} with len {}", + process_info, info.0.icon_name, info.1 + ); + + info.0.icon_dataurl = du.1; + + APP_INFO_CACHE + .write() + .unwrap() + .insert(process_info.exec_path, Some(info.0.clone())); + + return Ok(info.0); + } + Err(err) => { + error!( + "{}: failed to get icon: {}", + info.0.icon_name, + err.to_string() + ); + } + }; + } + + Err(Error::new(ErrorKind::NotFound, format!("failed to find app info")).into()) + } +} + +/// Returns a vector of application directories that are expected +/// to contain all .desktop files the current user has access to. +/// The result of this function is cached for 5 minutes as it's not expected +/// that application directories actually change. +#[once(time = 300, sync_writes = true, result = true)] +fn get_application_directories() -> Result<Vec<PathBuf>> { + let xdg_home = match env::var_os("XDG_DATA_HOME") { + Some(path) => PathBuf::from(path), + None => { + let home = dirs::home_dir() + .ok_or(Error::new(ErrorKind::Other, "Failed to get home directory"))?; + + home.join(".local/share") + } + }; + + let extra_application_dirs = match env::var_os("XDG_DATA_DIRS") { + Some(paths) => env::split_paths(&paths).map(PathBuf::from).collect(), + None => { + // Fallback if XDG_DATA_DIRS is not set. If it's set, it normally already contains /usr/share and + // /usr/local/share + vec![ + PathBuf::from("/usr/share"), + PathBuf::from("/usr/local/share"), + ] + } + }; + + let mut app_dirs = Vec::new(); + for extra_dir in extra_application_dirs { + app_dirs.push(extra_dir.join("applications")); + } + + app_dirs.push(xdg_home.join("applications")); + + Ok(app_dirs) +} + +// TODO(ppacher): cache the result of find_desktop_files as well. +// Though, seems like we cannot use the #[cached::proc_macro::cached] or #[cached::proc_macro::once] macros here +// because [`Result<Vec<fs::DirEntry>>>`] does not implement [`Clone`] +fn find_desktop_files(path: &Path) -> Result<Vec<fs::DirEntry>> { + match path.read_dir() { + Ok(files) => { + let desktop_files = files + .filter_map(|entry| entry.ok()) + .filter(|entry| match entry.file_type() { + Ok(ft) => ft.is_file() || ft.is_symlink(), + _ => false, + }) + .filter(|entry| entry.file_name().to_string_lossy().ends_with(".desktop")) + .collect::<Vec<_>>(); + + Ok(desktop_files) + } + Err(err) => { + // We ignore NotFound errors here because not all application + // directories need to exist. + if err.kind() == ErrorKind::NotFound { + Ok(Vec::new()) + } else { + Err(err.into()) + } + } + } +} + +enum CheckType { + Name, + Exec, +} + +fn try_get_app_info( + needle: &str, + check: CheckType, + desktop_files: &Vec<fs::DirEntry>, +) -> Result<Vec<(AppInfo, usize)>> { + let path = PathBuf::from(needle); + + let file_name = path.as_path().file_name().unwrap_or_default().to_str(); + + let mut result = Vec::new(); + + for file in desktop_files { + let content = Ini::load_from_file_opt( + file.path(), + ParseOption { + enabled_escape: false, + enabled_quote: true, + }, + ) + .map_err(|err| Error::new(ErrorKind::Other, err.to_string()))?; + + let desktop_section = match content.section(Some("Desktop Entry")) { + Some(section) => section, + None => { + continue; + } + }; + + let matches = match check { + CheckType::Name => { + let name = match desktop_section.get("Name") { + Some(name) => name, + None => { + continue; + } + }; + + if let Some(file_name) = file_name { + if name.to_lowercase().contains(file_name) { + file_name.len() + } else { + 0 + } + } else { + 0 + } + } + CheckType::Exec => { + let exec = match desktop_section.get("Exec") { + Some(exec) => exec, + None => { + continue; + } + }; + + if exec.to_lowercase().contains(needle) { + needle.len() + } else if let Some(file_name) = file_name { + if exec.to_lowercase().starts_with(file_name) { + file_name.len() + } else { + 0 + } + } else { + 0 + } + } + }; + + if matches > 0 { + debug!( + "[xdg] found matching desktop for needle {} file at {}", + needle, + file.path().to_string_lossy() + ); + + let info = parse_app_info(desktop_section); + + result.push((info, matches)); + } + } + + if result.len() > 0 { + Ok(result) + } else { + Err(Error::new(ErrorKind::NotFound, "no matching .desktop files found").into()) + } +} + +fn parse_app_info(props: &ini::Properties) -> AppInfo { + AppInfo { + icon_dataurl: "".to_string(), + app_name: props.get("Name").unwrap_or_default().to_string(), + comment: props.get("Comment").unwrap_or_default().to_string(), + icon_name: props.get("Icon").unwrap_or_default().to_string(), + } +} + +fn get_icon_as_png_dataurl(name: &str, size: i8) -> Result<(String, String)> { + unsafe { + if GTK_DEFAULT_THEME.is_none() { + let theme = gtk_icon_theme_get_default(); + if theme.is_null() { + debug!("You have to initialize GTK!"); + return Err(Error::new(ErrorKind::Other, "You have to initialize GTK!").into()); + } + + let theme = gtk_icon_theme_get_default(); + GTK_DEFAULT_THEME = Some(theme); + } + } + + let mut icons = Vec::new(); + + // push the name + icons.push(name); + + // if we don't find the icon by it's name and it includes an extension, + // drop the extension and try without. + let name_without_ext; + if let Some(ext) = PathBuf::from(name).extension() { + let ext = ext.to_str().unwrap(); + + let mut ext_dot = String::from(".").to_owned(); + ext_dot.push_str(ext); + + name_without_ext = name.replace(ext_dot.as_str(), ""); + icons.push(name_without_ext.as_str()); + } else { + name_without_ext = String::from(name); + } + + // The xdg-desktop icon specification allows a fallback for icons that contains dashes. + // i.e. the following lookup order is used: + // - network-wired-secure + // - network-wired + // - network + // + name_without_ext + .split("-") + .for_each(|part| icons.push(part)); + + for name in icons { + debug!("trying to load icon {}", name); + + unsafe { + let c_str = CString::new(name).unwrap(); + + let icon_info = gtk_icon_theme_lookup_icon( + GTK_DEFAULT_THEME.unwrap(), + c_str.as_ptr() as *const i8, + size as c_int, + 0, + ); + if icon_info.is_null() { + error!("failed to lookup icon {}", name); + + continue; + } + + let filename = gtk_icon_info_get_filename(icon_info); + + let filename = CStr::from_ptr(filename).to_str().unwrap().to_string(); + + gtk_icon_info_free(icon_info); + + match read_and_convert_pixbuf(filename.clone()) { + Ok(pb) => return Ok((filename, pb)), + Err(err) => { + error!("failed to load icon from {}: {}", filename, err.to_string()); + + continue; + } + } + } + } + + Err(Error::new(ErrorKind::NotFound, "failed to find icon").into()) +} + +/* +fn get_icon_as_file_2(ext: &str, size: i32) -> io::Result<(String, Vec<u8>)> { + let result: String; + let buf: Vec<u8>; + + unsafe { + let filename = CString::new(ext).unwrap(); + let null: u8 = 0; + let p_null = &null as *const u8; + let nullsize: usize = 0; + let mut res = 0; + let p_res = &mut res as *mut i32; + let p_res = gio_sys::g_content_type_guess(filename.as_ptr(), p_null, nullsize, p_res); + let icon = gio_sys::g_content_type_get_icon(p_res); + g_free(p_res as *mut c_void); + if DEFAULT_THEME.is_none() { + let theme = gtk_icon_theme_get_default(); + if theme.is_null() { + println!("You have to initialize GTK!"); + return Err(io::Error::new(io::ErrorKind::Other, "You have to initialize GTK!")) + } + let theme = gtk_icon_theme_get_default(); + DEFAULT_THEME = Some(theme); + } + let icon_names = gio_sys::g_themed_icon_get_names(icon as *mut GThemedIcon) as *mut *const i8; + let icon_info = gtk_icon_theme_choose_icon(DEFAULT_THEME.unwrap(), icon_names, size, GTK_ICON_LOOKUP_NO_SVG); + let filename = gtk_icon_info_get_filename(icon_info); + + gtk_icon_info_free(icon_info); + + result = CStr::from_ptr(filename).to_str().unwrap().to_string(); + + buf = match read_and_convert_pixbuf(result.clone()) { + Ok(pb) => pb, + Err(_) => Vec::new(), + }; + + g_object_unref(icon as *mut GObject); + } + + Ok((result, buf)) + +} +*/ + +fn read_and_convert_pixbuf(result: String) -> std::result::Result<String, glib::Error> { + let pixbuf = match Pixbuf::from_file(result.clone()) { + Ok(data) => Ok(data), + Err(err) => { + error!("failed to load icon pixbuf: {}", err.to_string()); + + Pixbuf::from_resource(result.clone().as_str()) + } + }; + + match pixbuf { + Ok(data) => match data.save_to_bufferv("png", &[]) { + Ok(data) => { + let mut du = DataUrl::new(); + + du.set_media_type(Some("image/png".to_string())); + du.set_data(&data); + + Ok(du.to_string()) + } + Err(err) => { + return Err(glib::Error::new( + PixbufError::Failed, + err.to_string().as_str(), + )); + } + }, + Err(err) => Err(err), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ctor::ctor; + use log::warn; + use which::which; + + // Use the ctor create to setup a global initializer before our tests are executed. + #[ctor] + fn init() { + // we need to initialize GTK before running our tests. + // This is only required when unit tests are executed as + // GTK will otherwise be initialize by Tauri. + + gtk::init().expect("failed to initialize GTK for tests") + } + + #[test] + fn test_find_info_success() { + // we expect at least one of the following binaries to be installed + // on a linux system + let test_binaries = vec![ + "vim", // vim is mostly bundled with a .desktop file + "blueman-manager", // blueman-manager is the default bluetooth manager on most DEs + "nautilus", // nautlis: file-manager on GNOME DE + "thunar", // thunar: file-manager on XFCE + "dolphin", // dolphin: file-manager on KDE + ]; + + let mut bin_found = false; + + for cmd in test_binaries { + match which(cmd) { + Ok(bin) => { + bin_found = true; + + let bin = bin.to_string_lossy().to_string(); + + let result = get_app_info(ProcessInfo { + cmdline: cmd.to_string(), + exec_path: bin.clone(), + matching_path: bin.clone(), + pid: 0, + }) + .expect( + format!( + "expected to find app info for {} ({})", + bin, + cmd.to_string() + ) + .as_str(), + ); + + let empty_string = String::from(""); + + // just make sure all fields are populated + assert_ne!(result.app_name, empty_string); + assert_ne!(result.comment, empty_string); + assert_ne!(result.icon_name, empty_string); + assert_ne!(result.icon_dataurl, empty_string); + } + Err(_) => { + // binary not found + continue; + } + } + } + + if !bin_found { + warn!("test_find_info_success: no test binary found, test was skipped") + } + } +} diff --git a/desktop/tauri/src-tauri/tauri.conf.json b/desktop/tauri/src-tauri/tauri.conf.json new file mode 100644 index 00000000..0fca6f4f --- /dev/null +++ b/desktop/tauri/src-tauri/tauri.conf.json @@ -0,0 +1,106 @@ +{ + "build": { + "beforeDevCommand": { + "script": "npm run tauri-dev", + "cwd": "../../angular", + "wait": false + }, + "devPath": "http://localhost:4100", + "distDir": "../../angular/dist/tauri-builtin", + "withGlobalTauri": true + }, + "package": { + "productName": "Portmaster", + "version": "0.1.0" + }, + "plugins": { + "cli": { + "args": [ + { + "short": "d", + "name": "data", + "description": "Path to the installation directory", + "takesValue": true + }, + { + "short": "b", + "name": "background", + "description": "Start in the background without opening a window" + }, + { + "name": "with-notifications", + "description": "Enable experimental notifications via Tauri. Replaces the notifier app." + }, + { + "name": "with-prompts", + "description": "Enable experimental prompt support via Tauri. Replaces the notifier app." + } + ] + } + }, + "tauri": { + "bundle": { + "active": true, + "category": "Utility", + "copyright": "", + "deb": { + "depends": [] + }, + "externalBin": [ + "binaries/portmaster-start", + "binaries/portmaster-core" + ], + "icon": [ + "../assets/icons/pm_dark_512.png", + "../assets/icons/pm_dark_512.ico", + "../assets/icons/pm_light_512.png", + "../assets/icons/pm_light_512.ico" + ], + "identifier": "io.safing.portmaster", + "longDescription": "", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [], + "shortDescription": "", + "targets": [ + "deb", + "appimage", + "nsis", + "msi", + "app" + ], + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "" + } + }, + "security": { + "csp": null, + "dangerousRemoteDomainIpcAccess": [ + { + "windows": [ + "main", + "prompt" + ], + "plugins": [ + "shell", + "os", + "clipboard-manager", + "event", + "window", + "cli", + "portmaster" + ], + "domain": "localhost" + } + ] + }, + "windows": [] + } +} \ No newline at end of file