Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Michael Pfaff | acb2d38d8a | |
Michael Pfaff | b4b6b292d0 | |
Michael Pfaff | e7a3ce4976 | |
Michael Pfaff | 4ad392d83a | |
Michael Pfaff | 1c798dbcae |
|
@ -32,6 +32,18 @@ version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-scoped"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1181d2a07303ac3e8df0b3bdaaf648b4ac968d352e61158f5c1897db70d22a09"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"pin-project",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -135,6 +147,26 @@ version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.31"
|
version = "0.8.31"
|
||||||
|
@ -184,6 +216,21 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -191,6 +238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -199,6 +247,34 @@ version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -217,10 +293,27 @@ version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -250,9 +343,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -356,9 +449,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.8.2"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
@ -510,8 +603,8 @@ checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -532,6 +625,16 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.28.4"
|
version = "0.28.4"
|
||||||
|
@ -543,9 +646,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.12.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
|
@ -598,12 +704,45 @@ version = "3.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
|
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
@ -711,6 +850,17 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.6"
|
version = "1.5.6"
|
||||||
|
@ -792,20 +942,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "school-computer-toolkit"
|
name = "school-computer-toolkit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-scoped",
|
||||||
|
"dirs",
|
||||||
|
"futures-util",
|
||||||
"miette",
|
"miette",
|
||||||
|
"once_cell",
|
||||||
"ramhorns",
|
"ramhorns",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -889,6 +1044,12 @@ version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smawk"
|
name = "smawk"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -1024,6 +1185,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
"mio",
|
||||||
|
"num_cpus",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
|
@ -1184,6 +1346,12 @@ dependencies = [
|
||||||
"try-lock",
|
"try-lock",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
@ -1294,43 +1462,100 @@ version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.36.1",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.36.1",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.36.1",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.36.1",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc 0.42.0",
|
||||||
|
"windows_i686_gnu 0.42.0",
|
||||||
|
"windows_i686_msvc 0.42.0",
|
||||||
|
"windows_x86_64_gnu 0.42.0",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -6,11 +6,17 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
miette = { version = "4.7", features = [ "fancy" ] }
|
async-scoped = { version = "0.7", features = ["use-tokio"] }
|
||||||
|
dirs = "4"
|
||||||
|
futures-util = { version = "0.3.21", default-features = false, features = ["alloc"] }
|
||||||
|
#indexmap = "1.9.1"
|
||||||
|
miette = { version = "4.7", features = ["fancy"] }
|
||||||
|
once_cell = { version = "1.16", features = ["parking_lot"] }
|
||||||
ramhorns = "0.14"
|
ramhorns = "0.14"
|
||||||
reqwest = { version = "0.11.10", features = [ "json" ] }
|
reqwest = { version = "0.11.10", features = ["json"] }
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tokio = { version = "1.19", features = [ "fs", "macros", "process", "rt" ] }
|
tokio = { version = "1.19", features = ["fs", "macros", "process", "rt", "rt-multi-thread"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
windows-sys = { version = "0.42", features = ["Win32_Foundation", "Win32_System_Registry", "Win32_System_WindowsProgramming"] }
|
||||||
|
|
||||||
|
|
337
src/install.rs
337
src/install.rs
|
@ -6,29 +6,52 @@ use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
const USER_AGENT_STR: &'static str =
|
pub fn get_username() -> Result<String> {
|
||||||
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
|
const CAPACITY: u32 = 32;
|
||||||
|
let mut len: u32 = CAPACITY;
|
||||||
|
const LAYOUT: std::alloc::Layout =
|
||||||
|
unsafe { std::alloc::Layout::from_size_align_unchecked(CAPACITY as usize, 1) };
|
||||||
|
let ptr = unsafe { std::alloc::alloc(LAYOUT) };
|
||||||
|
ensure!(!ptr.is_null(), "Buffer allocation failed");
|
||||||
|
let success = unsafe {
|
||||||
|
windows_sys::Win32::System::WindowsProgramming::GetUserNameA(ptr, &mut len as *mut u32) == 1
|
||||||
|
};
|
||||||
|
ensure!(success, "GetUserNameA failed");
|
||||||
|
assert!(len <= CAPACITY, "Buffer overflow caught");
|
||||||
|
String::from_utf8(unsafe { Vec::from_raw_parts(ptr, len as usize, CAPACITY as usize) })
|
||||||
|
.into_diagnostic()
|
||||||
|
}
|
||||||
|
|
||||||
/// A sequential pipeline of [`Step`]s.
|
/// A sequential pipeline of [`Step`]s.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Pipeline<'a> {
|
pub struct Pipeline<'a> {
|
||||||
name: &'a str,
|
pub name: Option<&'a str>,
|
||||||
steps: Vec<Step<'a>>,
|
pub steps: &'a [Step<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Pipeline<'a> {
|
impl<'a> Pipeline<'a> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(name: &'a str, steps: impl Into<Vec<Step<'a>>>) -> Self {
|
pub fn new(name: &'a str, steps: &'a [Step<'a>]) -> Self {
|
||||||
Self {
|
Self::of(steps).named(name)
|
||||||
name,
|
}
|
||||||
steps: steps.into(),
|
|
||||||
}
|
#[inline(always)]
|
||||||
|
pub fn of(steps: &'a [Step<'a>]) -> Self {
|
||||||
|
Self { name: None, steps }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn named(mut self, name: &'a str) -> Self {
|
||||||
|
self.name = Some(name);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
|
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
|
||||||
println!("Invoking {}...", self.name);
|
if let Some(name) = self.name {
|
||||||
|
println!("Invoking {name}...");
|
||||||
|
}
|
||||||
|
|
||||||
for step in self.steps.iter() {
|
for step in self.steps.into_iter() {
|
||||||
step.invoke(ctx).await?;
|
step.invoke(ctx).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +59,7 @@ impl<'a> Pipeline<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum Step<'a> {
|
pub enum Step<'a> {
|
||||||
DownloadFile {
|
DownloadFile {
|
||||||
/// Remote resourcee to download from.
|
/// Remote resourcee to download from.
|
||||||
|
@ -59,6 +82,18 @@ pub enum Step<'a> {
|
||||||
args: &'a [&'a str],
|
args: &'a [&'a str],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
InstallMsi {
|
||||||
|
file: Cow<'a, Path>,
|
||||||
|
|
||||||
|
props: Cow<'a, [Cow<'a, str>]>,
|
||||||
|
},
|
||||||
|
|
||||||
|
CreateDirectory {
|
||||||
|
target: Cow<'a, Path>,
|
||||||
|
|
||||||
|
parents: bool,
|
||||||
|
},
|
||||||
|
|
||||||
CreateShortcut {
|
CreateShortcut {
|
||||||
/// Target of the shortcut (i.e. what is points to).
|
/// Target of the shortcut (i.e. what is points to).
|
||||||
target: ShortcutTarget<'a>,
|
target: ShortcutTarget<'a>,
|
||||||
|
@ -66,11 +101,64 @@ pub enum Step<'a> {
|
||||||
/// Path of the created shortcut file.
|
/// Path of the created shortcut file.
|
||||||
file: Cow<'a, Path>,
|
file: Cow<'a, Path>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Nukes a publicly accessible directory and locks it from further modification.
|
||||||
|
Nuke { target: Cow<'a, Path> },
|
||||||
|
|
||||||
|
/// Executes the steps concurrently.
|
||||||
|
Concurrent(&'a [Pipeline<'a>]),
|
||||||
|
|
||||||
|
/// Appends the path to the user-wide PATH environment variable.
|
||||||
|
AppendPath(Cow<'a, Path>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Clone for Step<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Concurrent(pipelines) => Self::Concurrent(pipelines.clone()),
|
||||||
|
Self::DownloadFile { res, file } => Self::DownloadFile {
|
||||||
|
res: res.clone(),
|
||||||
|
file: file.clone(),
|
||||||
|
},
|
||||||
|
Self::ExtractFile { file, dest } => Self::ExtractFile {
|
||||||
|
file: file.clone(),
|
||||||
|
dest: dest.clone(),
|
||||||
|
},
|
||||||
|
Self::ExecuteCommand { file, args } => Self::ExecuteCommand {
|
||||||
|
file: file.clone(),
|
||||||
|
args: args.clone(),
|
||||||
|
},
|
||||||
|
Self::InstallMsi { file, props } => Self::InstallMsi {
|
||||||
|
file: file.clone(),
|
||||||
|
props: props.clone(),
|
||||||
|
},
|
||||||
|
Self::CreateDirectory { target, parents } => Self::CreateDirectory {
|
||||||
|
target: target.clone(),
|
||||||
|
parents: *parents,
|
||||||
|
},
|
||||||
|
Self::CreateShortcut { target, file } => Self::CreateShortcut {
|
||||||
|
target: target.clone(),
|
||||||
|
file: file.clone(),
|
||||||
|
},
|
||||||
|
Self::Nuke { target } => Self::Nuke {
|
||||||
|
target: target.clone(),
|
||||||
|
},
|
||||||
|
Self::AppendPath(path) => Self::AppendPath(path.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Step<'a> {
|
impl<'a> Step<'a> {
|
||||||
|
#[inline]
|
||||||
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
|
pub async fn invoke(&self, ctx: &Context) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Concurrent(sequences) => {
|
||||||
|
println!("Executing concurrent steps...");
|
||||||
|
if let Err(e) = invoke_parallel(ctx, sequences).await {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::DownloadFile { res, file } => {
|
Self::DownloadFile { res, file } => {
|
||||||
if file.exists() {
|
if file.exists() {
|
||||||
println!(
|
println!(
|
||||||
|
@ -114,7 +202,6 @@ impl<'a> Step<'a> {
|
||||||
let mut resp = ctx
|
let mut resp = ctx
|
||||||
.reqwest
|
.reqwest
|
||||||
.get(url)
|
.get(url)
|
||||||
.header("User-Agent", USER_AGENT_STR)
|
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
|
@ -190,6 +277,38 @@ impl<'a> Step<'a> {
|
||||||
.wrap_err(EXECUTE_COMMAND_ERROR_MSG)?;
|
.wrap_err(EXECUTE_COMMAND_ERROR_MSG)?;
|
||||||
ensure!(status.success(), EXECUTE_COMMAND_ERROR_MSG);
|
ensure!(status.success(), EXECUTE_COMMAND_ERROR_MSG);
|
||||||
}
|
}
|
||||||
|
Self::InstallMsi { file, props } => {
|
||||||
|
println!(
|
||||||
|
"Installing MSI {file} with props {props:?}`...",
|
||||||
|
file = file.to_str().unwrap_or("<NON UTF-8>"),
|
||||||
|
props = props.iter().map(|s| s.as_ref()).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
const ERROR_MSG: &'static str = "Installing MSI failed.";
|
||||||
|
let status = tokio::process::Command::new("msiexec.exe")
|
||||||
|
.args(["/qn", "/i"])
|
||||||
|
.arg(file.as_os_str())
|
||||||
|
.args(props.into_iter().map(|s| s.as_ref()))
|
||||||
|
.stdout(std::process::Stdio::inherit())
|
||||||
|
.stderr(std::process::Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err(ERROR_MSG)?;
|
||||||
|
ensure!(status.success(), ERROR_MSG);
|
||||||
|
}
|
||||||
|
Self::CreateDirectory { target, parents } => {
|
||||||
|
if target.is_dir() {
|
||||||
|
println!("Directory {target:?} already created.");
|
||||||
|
} else {
|
||||||
|
if *parents {
|
||||||
|
std::fs::create_dir_all(target)
|
||||||
|
} else {
|
||||||
|
std::fs::create_dir(target)
|
||||||
|
}
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Create directory failed.")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::CreateShortcut { target, file } => {
|
Self::CreateShortcut { target, file } => {
|
||||||
println!(
|
println!(
|
||||||
"Creating shortcut to {target:?} at {file}...",
|
"Creating shortcut to {target:?} at {file}...",
|
||||||
|
@ -215,7 +334,6 @@ impl<'a> Step<'a> {
|
||||||
.wrap_err(CREATE_SHORTCUT_ERROR_MSG)?
|
.wrap_err(CREATE_SHORTCUT_ERROR_MSG)?
|
||||||
}
|
}
|
||||||
ShortcutTarget::Executable { file: exec_file, args } => {
|
ShortcutTarget::Executable { file: exec_file, args } => {
|
||||||
use std::fmt::Write;
|
|
||||||
tokio::process::Command::new("powershell")
|
tokio::process::Command::new("powershell")
|
||||||
.arg("-Command")
|
.arg("-Command")
|
||||||
.arg(format!(r#"$shell = New-Object -ComObject WScript.Shell; $shortcut = $shell.CreateShortcut({file:?}); $shortcut.TargetPath = {exec_file:?}; $shortcut.Arguments = {args:?}; $shortcut.Save()"#))
|
.arg(format!(r#"$shell = New-Object -ComObject WScript.Shell; $shortcut = $shell.CreateShortcut({file:?}); $shortcut.TargetPath = {exec_file:?}; $shortcut.Arguments = {args:?}; $shortcut.Save()"#))
|
||||||
|
@ -229,6 +347,152 @@ impl<'a> Step<'a> {
|
||||||
};
|
};
|
||||||
ensure!(status.success(), CREATE_SHORTCUT_ERROR_MSG);
|
ensure!(status.success(), CREATE_SHORTCUT_ERROR_MSG);
|
||||||
}
|
}
|
||||||
|
Self::Nuke { target } => {
|
||||||
|
println!(
|
||||||
|
"Nuking {target}...",
|
||||||
|
target = target.to_str().unwrap_or("<NON UTF-8>"),
|
||||||
|
);
|
||||||
|
// first delete
|
||||||
|
if target.is_dir() {
|
||||||
|
std::fs::remove_dir_all(target)
|
||||||
|
} else {
|
||||||
|
std::fs::remove_file(target)
|
||||||
|
}
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Nuke failed: Could not remove target")?;
|
||||||
|
|
||||||
|
// then make new
|
||||||
|
std::fs::create_dir_all(target)
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Nuke failed: Could not create directory")?;
|
||||||
|
|
||||||
|
let mut grant = get_username()?;
|
||||||
|
grant.push_str(":F");
|
||||||
|
let status = tokio::process::Command::new("cacls")
|
||||||
|
.arg(target.as_ref())
|
||||||
|
.arg("/T")
|
||||||
|
.arg("/G")
|
||||||
|
.arg(grant)
|
||||||
|
.stdout(std::process::Stdio::inherit())
|
||||||
|
.stderr(std::process::Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Nuke failed: could not set permissions")?;
|
||||||
|
ensure!(status.success(), "Nuke failed: could not set permissions");
|
||||||
|
}
|
||||||
|
Self::AppendPath(path) => {
|
||||||
|
static LOCK: once_cell::sync::Lazy<tokio::sync::Mutex<()>> =
|
||||||
|
once_cell::sync::Lazy::new(|| tokio::sync::Mutex::new(()));
|
||||||
|
|
||||||
|
const HKEY: windows_sys::Win32::System::Registry::HKEY =
|
||||||
|
windows_sys::Win32::System::Registry::HKEY_CURRENT_USER;
|
||||||
|
const SUBKEY: &str = "Environment";
|
||||||
|
const VALUE: &str = "PATH";
|
||||||
|
const TYPE: windows_sys::Win32::System::Registry::RRF_RT =
|
||||||
|
windows_sys::Win32::System::Registry::RRF_RT_REG_SZ;
|
||||||
|
|
||||||
|
const CAPACITY: usize = 1024;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Appending {path} to the PATH environment variable...",
|
||||||
|
path = path.to_str().unwrap_or("<NON UTF-8>"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut buffer: [std::mem::MaybeUninit<u8>; CAPACITY] =
|
||||||
|
std::mem::MaybeUninit::uninit_array();
|
||||||
|
let mut len: u32 = CAPACITY as u32;
|
||||||
|
|
||||||
|
// this lock will be held until the end of the scope. We do this to prevent
|
||||||
|
// concurrent access to the registry from interfering with (i.e. overwriting)
|
||||||
|
// eachother.
|
||||||
|
let _lock = LOCK.lock().await;
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
windows_sys::Win32::System::Registry::RegGetValueA(
|
||||||
|
HKEY,
|
||||||
|
SUBKEY.as_ptr(),
|
||||||
|
VALUE.as_ptr(),
|
||||||
|
TYPE,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
&mut buffer as *mut _ as *mut _,
|
||||||
|
&mut len as *mut _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
err == windows_sys::Win32::Foundation::ERROR_SUCCESS,
|
||||||
|
"RegGetValueA failed: {err}"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(len <= CAPACITY as u32, "Buffer overflow caught");
|
||||||
|
let buffer: &mut [u8] = unsafe {
|
||||||
|
std::mem::MaybeUninit::slice_assume_init_mut(&mut buffer[..len as usize])
|
||||||
|
};
|
||||||
|
let path = path
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| miette!("Path is not ASCII"))?;
|
||||||
|
ensure!(path.is_ascii(), "Path is not ASCII");
|
||||||
|
let path_b = path.as_bytes();
|
||||||
|
let contains = buffer.split(|b| *b == ';' as u8).any(|item| item == path_b);
|
||||||
|
if contains {
|
||||||
|
println!("Not adding {path} because it is already in the PATH");
|
||||||
|
} else {
|
||||||
|
let mut buffer = Vec::from(buffer);
|
||||||
|
if buffer.is_empty() {
|
||||||
|
buffer.push(';' as u8);
|
||||||
|
}
|
||||||
|
buffer.extend(path_b);
|
||||||
|
|
||||||
|
let mut hkey = std::mem::MaybeUninit::uninit();
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
windows_sys::Win32::System::Registry::RegOpenKeyExA(
|
||||||
|
HKEY,
|
||||||
|
SUBKEY.as_ptr(),
|
||||||
|
0,
|
||||||
|
windows_sys::Win32::System::Registry::KEY_SET_VALUE,
|
||||||
|
hkey.as_mut_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
err == windows_sys::Win32::Foundation::ERROR_SUCCESS,
|
||||||
|
"RegOpenKeyExA failed: {err}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// SAFETY: we just opened the key (which sets the handle) and checked for errors.
|
||||||
|
let hkey = unsafe { hkey.assume_init() };
|
||||||
|
|
||||||
|
ensure!(buffer.len() < u32::MAX as usize, "Buffer is too large: {}", buffer.len());
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
windows_sys::Win32::System::Registry::RegSetValueExA(
|
||||||
|
hkey,
|
||||||
|
VALUE.as_ptr(),
|
||||||
|
0,
|
||||||
|
TYPE,
|
||||||
|
buffer.as_ptr(),
|
||||||
|
buffer.len() as u32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
err == windows_sys::Win32::Foundation::ERROR_SUCCESS,
|
||||||
|
"RegSetValueExA failed: {err}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
windows_sys::Win32::System::Registry::RegCloseKey(hkey)
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
err == windows_sys::Win32::Foundation::ERROR_SUCCESS,
|
||||||
|
"RegCloseKey failed: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println!("-> Done.");
|
println!("-> Done.");
|
||||||
|
|
||||||
|
@ -236,9 +500,47 @@ impl<'a> Step<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this part needed to be isolated to shut work around an async lowering recursion bug.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn spawn_parallel<'a, 'b, 'c>(
|
||||||
|
ctx: &'b Context,
|
||||||
|
sequences: &'b [Pipeline<'c>],
|
||||||
|
) -> async_scoped::Scope<'a, Result<()>, async_scoped::Tokio>
|
||||||
|
where
|
||||||
|
'b: 'a,
|
||||||
|
'c: 'a,
|
||||||
|
{
|
||||||
|
async_scoped::Scope::scope(|scope| {
|
||||||
|
for seq in sequences {
|
||||||
|
scope.spawn(seq.invoke(ctx));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
async fn invoke_parallel<'a>(ctx: &Context, sequences: &[Pipeline<'a>]) -> Result<()> {
|
||||||
|
// SAFETY: we immediately collect and block on it with no possibility of failure.
|
||||||
|
let mut results = unsafe { spawn_parallel(ctx, sequences) };
|
||||||
|
let results = tokio::task::block_in_place(|| {
|
||||||
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(results.collect())
|
||||||
|
});
|
||||||
|
let results = results.into_iter().map(|result| {
|
||||||
|
match result.into_diagnostic().wrap_err("Pipeline fork error.") {
|
||||||
|
Ok(Ok(t)) => Ok(t),
|
||||||
|
Ok(Err(e)) => Err(e),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
results.collect::<Result<()>>()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum RemoteResource<'a> {
|
pub enum RemoteResource<'a> {
|
||||||
// I'm not using url::Url here because it makes it impossible to make this const.
|
|
||||||
Url(&'a str),
|
Url(&'a str),
|
||||||
|
|
||||||
GitHubArtifact {
|
GitHubArtifact {
|
||||||
|
@ -342,9 +644,8 @@ async fn fetch_latest_release<'a, 'b>(
|
||||||
))
|
))
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err("Invalid GitHub repo for download step.")?;
|
.wrap_err("Invalid GitHub repo for download step.")?;
|
||||||
let mut resp = reqwest
|
let resp = reqwest
|
||||||
.get(url)
|
.get(url)
|
||||||
.header("User-Agent", USER_AGENT_STR)
|
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
|
|
401
src/main.rs
401
src/main.rs
|
@ -1,3 +1,6 @@
|
||||||
|
#![feature(maybe_uninit_slice)]
|
||||||
|
#![feature(maybe_uninit_uninit_array)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate miette;
|
extern crate miette;
|
||||||
|
|
||||||
|
@ -14,19 +17,34 @@ use install::{Pipeline, RemoteResource, ShortcutTarget, Step};
|
||||||
|
|
||||||
const SWTOOLS_PATH: &'static str = "C:\\SWTools";
|
const SWTOOLS_PATH: &'static str = "C:\\SWTools";
|
||||||
|
|
||||||
|
const USER_AGENT_STR: &'static str =
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
|
||||||
|
|
||||||
pub fn swtools_path() -> &'static Path {
|
pub fn swtools_path() -> &'static Path {
|
||||||
Path::new(SWTOOLS_PATH)
|
Path::new(SWTOOLS_PATH)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn desktop_path() -> PathBuf {
|
macro_rules! swtools_path {
|
||||||
Path::new("H:\\Profile\\Desktop").to_owned()
|
($( $path:literal )/+) => {
|
||||||
|
Path::new(concat!("C:\\SWTools" $(, "\\", $path )+))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn desktop_path() -> Option<PathBuf> {
|
||||||
|
dirs::desktop_dir()
|
||||||
|
.filter(|dir| dir.exists())
|
||||||
|
.or_else(|| Some(Path::new("H:\\Profile\\Desktop").to_owned()))
|
||||||
|
.filter(|dir| dir.exists())
|
||||||
|
.or_else(|| dirs::home_dir().map(|dir| dir.join("Desktop")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub reqwest: reqwest::Client,
|
pub reqwest: reqwest::Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
//#[tokio::main(flavor = "current_thread")]
|
||||||
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
println!("Bootstrapping...");
|
println!("Bootstrapping...");
|
||||||
|
|
||||||
|
@ -38,132 +56,291 @@ async fn main() -> Result<()> {
|
||||||
bail!("Could not find or access {}", SWTOOLS_PATH);
|
bail!("Could not find or access {}", SWTOOLS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nppp_zip = swtools_path().join("temp").join("notepad-plus-plus.zip");
|
let desktop_path = desktop_path()
|
||||||
let nppp_dir = swtools_path().join("notepad-plus-plus");
|
.ok_or_else(|| miette!("Could not find your desktop directory."))?;
|
||||||
|
|
||||||
let epp_zip = swtools_path().join("temp").join("explorer-plus-plus.zip");
|
let nppp_zip = swtools_path!("temp" / "notepad-plus-plus.zip");
|
||||||
let epp_dir = swtools_path().join("explorer-plus-plus");
|
let nppp_dir = swtools_path!("notepad-plus-plus");
|
||||||
|
|
||||||
let minecraft_dir = swtools_path().join("minecraft");
|
let arduino_zip = swtools_path!("temp" / "arduino.zip");
|
||||||
|
let arduino_dir = swtools_path!("arduino");
|
||||||
|
|
||||||
let psiphon_dir = swtools_path().join("psiphon");
|
let jdk_19_zip = swtools_path!("temp" / "jdk-19.zip");
|
||||||
let psiphon_bin = psiphon_dir.join("psiphon3.exe");
|
let jdk_19_dir = swtools_path!("jdk-19");
|
||||||
|
|
||||||
let utilities = [
|
let epp_zip = swtools_path!("temp" / "explorer-plus-plus.zip");
|
||||||
Pipeline::new(
|
let epp_dir = swtools_path!("explorer-plus-plus");
|
||||||
"Install Notepad++",
|
|
||||||
vec![
|
|
||||||
Step::DownloadFile {
|
|
||||||
file: nppp_zip.as_path().into(),
|
|
||||||
res: RemoteResource::GitHubArtifact {
|
|
||||||
repo: "notepad-plus-plus/notepad-plus-plus",
|
|
||||||
pattern: "npp.{{tag_name_strip_prefix}}.portable.x64.zip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Step::ExtractFile {
|
|
||||||
file: nppp_zip.as_path().into(),
|
|
||||||
dest: nppp_dir.as_path().into(),
|
|
||||||
},
|
|
||||||
Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Executable {
|
|
||||||
file: nppp_dir.join("notepad++.exe").into(),
|
|
||||||
|
|
||||||
args: "",
|
let minecraft_dir = swtools_path!("minecraft");
|
||||||
},
|
|
||||||
file: desktop_path().join("Notepad++.lnk").into(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Pipeline::new(
|
|
||||||
"Install Explorer++",
|
|
||||||
vec![
|
|
||||||
Step::DownloadFile {
|
|
||||||
file: epp_zip.as_path().into(),
|
|
||||||
res: RemoteResource::Url(
|
|
||||||
"https://explorerplusplus.com/software/explorer++_1.3.5_x64.zip",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Step::ExtractFile {
|
|
||||||
file: epp_zip.as_path().into(),
|
|
||||||
dest: epp_dir.as_path().into(),
|
|
||||||
},
|
|
||||||
Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Executable {
|
|
||||||
file: epp_dir.join("Explorer++.exe").into(),
|
|
||||||
|
|
||||||
args: "",
|
//let psiphon_dir = swtools_path!("psiphon");
|
||||||
},
|
let psiphon_bin = swtools_path!("psiphon" / "psiphon3.exe");
|
||||||
file: desktop_path().join("Explorer++.lnk").into(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Pipeline::new(
|
|
||||||
"Install Psiphon VPN",
|
|
||||||
vec![
|
|
||||||
Step::DownloadFile {
|
|
||||||
file: psiphon_bin.as_path().into(),
|
|
||||||
res: RemoteResource::Url(
|
|
||||||
"https://s3.amazonaws.com/f58p-mqce-k1yj/psiphon3.exe",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Executable {
|
|
||||||
file: psiphon_bin.as_path().into(),
|
|
||||||
args: "",
|
|
||||||
},
|
|
||||||
file: desktop_path().join("Psiphon3.lnk").into(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Pipeline::new(
|
|
||||||
"Install Minecraft (Java Edition)",
|
|
||||||
vec![
|
|
||||||
Step::DownloadFile {
|
|
||||||
file: minecraft_dir.join("Minecraft.exe").into(),
|
|
||||||
res: RemoteResource::Url("https://launcher.mojang.com/download/Minecraft.exe"),
|
|
||||||
},
|
|
||||||
Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Executable {
|
|
||||||
file: minecraft_dir.join("Minecraft.exe").into(),
|
|
||||||
|
|
||||||
args: "",
|
let rustup_init = swtools_path!("temp" / "rustup-init.exe");
|
||||||
},
|
|
||||||
file: desktop_path().join("Minecraft.lnk").into(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Pipeline::new(
|
|
||||||
"Create C:\\ Shortcut",
|
|
||||||
vec![Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Path {
|
|
||||||
path: Path::new("C:\\").into(),
|
|
||||||
},
|
|
||||||
file: desktop_path().join("OSDisk (C).lnk").into(),
|
|
||||||
}],
|
|
||||||
),
|
|
||||||
Pipeline::new(
|
|
||||||
"Create PowerShell Shortcut",
|
|
||||||
vec![Step::CreateShortcut {
|
|
||||||
target: ShortcutTarget::Executable {
|
|
||||||
file: Path::new("C:\\WINDOWS\\system32\\cmd.exe").into(),
|
|
||||||
|
|
||||||
args: "/C powershell",
|
let deno_zip = swtools_path!("temp" / "deno.zip");
|
||||||
},
|
let deno_dir = swtools_path!("deno");
|
||||||
file: desktop_path().join("PowerShell.lnk").into(),
|
//let deno_exe = swtools_path!("deno" / "deno.exe");
|
||||||
}],
|
|
||||||
),
|
let vscode_zip = swtools_path!("temp" / "vscode.zip");
|
||||||
|
let vscode_dir = swtools_path!("vscode");
|
||||||
|
|
||||||
|
let nppp_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: nppp_zip.into(),
|
||||||
|
res: RemoteResource::GitHubArtifact {
|
||||||
|
repo: "notepad-plus-plus/notepad-plus-plus",
|
||||||
|
pattern: "npp.{{tag_name_strip_prefix}}.portable.x64.zip",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Step::ExtractFile {
|
||||||
|
file: nppp_zip.into(),
|
||||||
|
dest: nppp_dir.into(),
|
||||||
|
},
|
||||||
|
Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: nppp_dir.join("notepad++.exe").into(),
|
||||||
|
|
||||||
|
args: "",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("Notepad++.lnk").into(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let epp_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: epp_zip.into(),
|
||||||
|
res: RemoteResource::Url(
|
||||||
|
"https://explorerplusplus.com/software/explorer++_1.3.5_x64.zip",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Step::ExtractFile {
|
||||||
|
file: epp_zip.into(),
|
||||||
|
dest: epp_dir.into(),
|
||||||
|
},
|
||||||
|
Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: epp_dir.join("Explorer++.exe").into(),
|
||||||
|
|
||||||
|
args: "",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("Explorer++.lnk").into(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let psiphon_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: psiphon_bin.into(),
|
||||||
|
res: RemoteResource::Url("https://s3.amazonaws.com/f58p-mqce-k1yj/psiphon3.exe"),
|
||||||
|
},
|
||||||
|
Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: psiphon_bin.into(),
|
||||||
|
args: "",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("Psiphon3.lnk").into(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let minecraft_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: minecraft_dir.join("minecraft.exe").into(),
|
||||||
|
res: RemoteResource::Url("https://launcher.mojang.com/download/Minecraft.exe"),
|
||||||
|
},
|
||||||
|
Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: minecraft_dir.join("minecraft.exe").into(),
|
||||||
|
|
||||||
|
args: "",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("Minecraft.lnk").into(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let c_drive_lnk_pl = [Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Path {
|
||||||
|
path: Path::new("C:\\").into(),
|
||||||
|
},
|
||||||
|
file: desktop_path.join("OSDisk (C).lnk").into(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let pwsh_pl = [Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: Path::new("C:\\WINDOWS\\system32\\cmd.exe").into(),
|
||||||
|
|
||||||
|
args: "/C powershell",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("PowerShell.lnk").into(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let arduino_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: arduino_zip.into(),
|
||||||
|
res: RemoteResource::GitHubArtifact {
|
||||||
|
repo: "arduino/arduino-ide",
|
||||||
|
pattern: "arduino-ide_{{tag_name}}_Windows_64bit.zip",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Step::ExtractFile {
|
||||||
|
file: arduino_zip.into(),
|
||||||
|
dest: arduino_dir.into(),
|
||||||
|
},
|
||||||
|
Step::CreateShortcut {
|
||||||
|
target: ShortcutTarget::Executable {
|
||||||
|
file: arduino_dir.join("Arduino IDE.exe").into(),
|
||||||
|
|
||||||
|
args: "",
|
||||||
|
},
|
||||||
|
file: desktop_path.join("Arduino IDE.lnk").into(),
|
||||||
|
},
|
||||||
|
// see https://docs.arduino.cc/software/ide-v1/tutorials/PortableIDE
|
||||||
|
// TODO: assess if this is actually necessary
|
||||||
|
//Step::CreateDirectory {
|
||||||
|
// target: arduino_dir.join("portable").into(),
|
||||||
|
// parents: false,
|
||||||
|
//}
|
||||||
|
];
|
||||||
|
|
||||||
|
let jdk_19_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: jdk_19_zip.into(),
|
||||||
|
// TODO: expand GitHubArtifact templating support and replace the hardcoded
|
||||||
|
// url with that.
|
||||||
|
//res: RemoteResource::GitHubArtifact {
|
||||||
|
// repo: "adoptium/temurin19-binaries",
|
||||||
|
// pattern: "OpenJDK19U-jdk_x64_windows_hotspot_19.0.1_10.zip arduino-ide_{{tag_name}}_Windows_64bit.zip",
|
||||||
|
//},
|
||||||
|
res: RemoteResource::Url(
|
||||||
|
"https://github.com/adoptium/temurin19-binaries/releases/download/jdk-19.0.1%2B10/OpenJDK19U-jdk_x64_windows_hotspot_19.0.1_10.zip",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Step::ExtractFile {
|
||||||
|
file: jdk_19_zip.into(),
|
||||||
|
dest: jdk_19_dir.into(),
|
||||||
|
},
|
||||||
|
Step::AppendPath(jdk_19_dir.join("bin").into()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let rust_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: rustup_init.into(),
|
||||||
|
res: RemoteResource::Url(
|
||||||
|
"https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Step::ExecuteCommand {
|
||||||
|
file: rustup_init.into(),
|
||||||
|
args: &[
|
||||||
|
"-y",
|
||||||
|
"--default-host",
|
||||||
|
"x86_64-pc-windows-gnu",
|
||||||
|
"--default-toolchain",
|
||||||
|
"nightly",
|
||||||
|
"--profile",
|
||||||
|
"default",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let deno_pl = [
|
||||||
|
Step::CreateDirectory {
|
||||||
|
target: deno_dir.into(),
|
||||||
|
|
||||||
|
parents: true,
|
||||||
|
},
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: deno_zip.into(),
|
||||||
|
res: RemoteResource::Url(
|
||||||
|
"https://github.com/denoland/deno/releases/latest/download/deno-x86_64-pc-windows-msvc.zip"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Step::ExtractFile {
|
||||||
|
file: deno_zip.into(),
|
||||||
|
dest: deno_dir.into(),
|
||||||
|
},
|
||||||
|
Step::AppendPath(deno_dir.into()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let vscode_pl = [
|
||||||
|
Step::DownloadFile {
|
||||||
|
file: vscode_zip.into(),
|
||||||
|
res: RemoteResource::Url("https://code.visualstudio.com/sha/download?build=stable&os=win32-x64-archive"),
|
||||||
|
},
|
||||||
|
Step::ExtractFile { file: vscode_zip.into(), dest: vscode_dir.into() },
|
||||||
|
];
|
||||||
|
|
||||||
|
let pipelines = [
|
||||||
|
Pipeline::new("Install Notepad++", nppp_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Explorer++", epp_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Psiphon VPN", psiphon_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Minecraft (Java Edition)", minecraft_pl.as_slice()),
|
||||||
|
Pipeline::new("Create C:\\ Shortcut", c_drive_lnk_pl.as_slice()),
|
||||||
|
Pipeline::new("Create PowerShell Shortcut", pwsh_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Arduino IDE v2", arduino_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Java (19/temurin)", jdk_19_pl.as_slice()),
|
||||||
|
Pipeline::new("Install Rust (nightly)", rust_pl.as_slice()),
|
||||||
|
// TODO: add an option to install from src
|
||||||
|
Pipeline::new("Install Deno (pre-compiled)", deno_pl.as_slice()),
|
||||||
|
Pipeline::new("Install VSCode", vscode_pl.as_slice()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut args = std::env::args_os();
|
||||||
|
let _ = args.next();
|
||||||
|
if let Some(arg) = args.next() {
|
||||||
|
match arg.to_str() {
|
||||||
|
Some("list") => {
|
||||||
|
println!("Pipelines:");
|
||||||
|
for pipeline in pipelines {
|
||||||
|
let name = pipeline.name.unwrap_or("Unnamed");
|
||||||
|
println!("\t{name}");
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Some("run") => {}
|
||||||
|
Some(cmd) => {
|
||||||
|
println!("Unrecognized command: {cmd}");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!("Unrecognized non-utf8 command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
reqwest: reqwest::Client::builder()
|
reqwest: reqwest::Client::builder()
|
||||||
|
.user_agent(USER_AGENT_STR)
|
||||||
.build()
|
.build()
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err("Could not initialize HTTP client.")?,
|
.wrap_err("Could not initialize HTTP client.")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
for utility in utilities {
|
let ctx = &ctx;
|
||||||
utility.invoke(&ctx).await?;
|
let (_, results) =
|
||||||
|
async_scoped::Scope::<'_, _, async_scoped::Tokio>::scope_and_block(move |scope| {
|
||||||
|
pipelines
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|pipeline| scope.spawn(async move { pipeline.invoke(ctx).await }))
|
||||||
|
});
|
||||||
|
let results =
|
||||||
|
results.into_iter().map(
|
||||||
|
|result| match result.into_diagnostic().wrap_err("Task error.") {
|
||||||
|
Ok(Ok(t)) => Ok(t),
|
||||||
|
Ok(Err(e)) => Err(e),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut had_error = false;
|
||||||
|
for result in results {
|
||||||
|
if let Err(e) = result {
|
||||||
|
had_error = true;
|
||||||
|
eprintln!("{e:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
if had_error {
|
||||||
|
Err(miette!("One or more errors in pipelines."))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue