214 lines
6.5 KiB
Rust
214 lines
6.5 KiB
Rust
#[macro_use]
|
|
extern crate miette;
|
|
|
|
#[macro_use]
|
|
extern crate serde;
|
|
|
|
mod install;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use miette::{IntoDiagnostic, Result, WrapErr};
|
|
|
|
use install::{Pipeline, RemoteResource, ShortcutTarget, Step};
|
|
|
|
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 {
|
|
Path::new(SWTOOLS_PATH)
|
|
}
|
|
|
|
pub fn desktop_path_fallible() -> 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")))
|
|
}
|
|
|
|
pub fn desktop_path() -> PathBuf {
|
|
desktop_path_fallible().expect("Desktop directory should have been resolved by now.")
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Context {
|
|
pub reqwest: reqwest::Client,
|
|
}
|
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
async fn main() -> Result<()> {
|
|
println!("Bootstrapping...");
|
|
|
|
if cfg!(not(windows)) {
|
|
bail!("Your platform is not supported.");
|
|
}
|
|
|
|
if !swtools_path().exists() {
|
|
bail!("Could not find or access {}", SWTOOLS_PATH);
|
|
}
|
|
|
|
if desktop_path_fallible().is_none() {
|
|
bail!("Could not find your desktop directory.");
|
|
}
|
|
|
|
let nppp_zip = swtools_path().join("temp").join("notepad-plus-plus.zip");
|
|
let nppp_dir = swtools_path().join("notepad-plus-plus");
|
|
|
|
let epp_zip = swtools_path().join("temp").join("explorer-plus-plus.zip");
|
|
let epp_dir = swtools_path().join("explorer-plus-plus");
|
|
|
|
let minecraft_dir = swtools_path().join("minecraft");
|
|
|
|
let psiphon_dir = swtools_path().join("psiphon");
|
|
let psiphon_bin = psiphon_dir.join("psiphon3.exe");
|
|
|
|
let pipelines = [
|
|
Pipeline::new(
|
|
"Install Notepad++",
|
|
vec![
|
|
Step::DownloadFile {
|
|
file: nppp_zip.clone().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.clone().into(),
|
|
dest: nppp_dir.clone().into(),
|
|
},
|
|
Step::CreateShortcut {
|
|
target: ShortcutTarget::Executable {
|
|
file: nppp_dir.join("notepad++.exe").into(),
|
|
|
|
args: "",
|
|
},
|
|
file: desktop_path().join("Notepad++.lnk").into(),
|
|
},
|
|
],
|
|
),
|
|
Pipeline::new(
|
|
"Install Explorer++",
|
|
vec![
|
|
Step::DownloadFile {
|
|
file: epp_zip.clone().into(),
|
|
res: RemoteResource::Url(
|
|
"https://explorerplusplus.com/software/explorer++_1.3.5_x64.zip",
|
|
),
|
|
},
|
|
Step::ExtractFile {
|
|
file: epp_zip.clone().into(),
|
|
dest: epp_dir.clone().into(),
|
|
},
|
|
Step::CreateShortcut {
|
|
target: ShortcutTarget::Executable {
|
|
file: epp_dir.join("Explorer++.exe").into(),
|
|
|
|
args: "",
|
|
},
|
|
file: desktop_path().join("Explorer++.lnk").into(),
|
|
},
|
|
],
|
|
),
|
|
Pipeline::new(
|
|
"Install Psiphon VPN",
|
|
vec![
|
|
Step::DownloadFile {
|
|
file: psiphon_bin.clone().into(),
|
|
res: RemoteResource::Url(
|
|
"https://s3.amazonaws.com/f58p-mqce-k1yj/psiphon3.exe",
|
|
),
|
|
},
|
|
Step::CreateShortcut {
|
|
target: ShortcutTarget::Executable {
|
|
file: psiphon_bin.clone().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: "",
|
|
},
|
|
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",
|
|
},
|
|
file: desktop_path().join("PowerShell.lnk").into(),
|
|
}],
|
|
),
|
|
];
|
|
|
|
let ctx = Context {
|
|
reqwest: reqwest::Client::builder()
|
|
.user_agent(USER_AGENT_STR)
|
|
.build()
|
|
.into_diagnostic()
|
|
.wrap_err("Could not initialize HTTP client.")?,
|
|
};
|
|
|
|
let results = futures_util::future::join_all(
|
|
pipelines
|
|
.into_iter()
|
|
.map(|pipeline| tokio::spawn({
|
|
let ctx = ctx.clone();
|
|
async move {
|
|
pipeline.invoke(&ctx).await
|
|
}
|
|
})),
|
|
)
|
|
.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:?}");
|
|
}
|
|
}
|
|
|
|
if had_error {
|
|
Err(miette!("One or more errors in pipelines."))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|