school-computer-toolkit/src/main.rs

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(())
}
}