feat: phase 2
Newfound motivation
This commit is contained in:
204
rust/src/pkg_store.rs
Normal file
204
rust/src/pkg_store.rs
Normal file
@ -0,0 +1,204 @@
|
||||
use std::collections::HashMap;
|
||||
use anyhow::{Result, anyhow};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, Emitter};
|
||||
use tokio::fs;
|
||||
use tokio::task::JoinSet;
|
||||
use crate::model::rainy;
|
||||
use crate::pkg::{Package, PkgKey};
|
||||
use crate::util;
|
||||
use crate::download_handler::DownloadHandler;
|
||||
|
||||
pub struct PackageStore {
|
||||
store: HashMap<PkgKey, Package>,
|
||||
has_fetched: bool,
|
||||
app: AppHandle,
|
||||
dlh: DownloadHandler
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Payload {
|
||||
pub pkg: PkgKey
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum InstallResult {
|
||||
Ready, Deferred
|
||||
}
|
||||
|
||||
impl PackageStore {
|
||||
pub fn new(app: AppHandle) -> PackageStore {
|
||||
PackageStore {
|
||||
store: HashMap::new(),
|
||||
has_fetched: false,
|
||||
app: app.clone(),
|
||||
dlh: DownloadHandler::new(app)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: PkgKey) -> Result<&Package> {
|
||||
self.store.get(&key)
|
||||
.ok_or_else(|| anyhow!("Invalid package key"))
|
||||
}
|
||||
|
||||
pub fn get_all(&self) -> HashMap<PkgKey, Package> {
|
||||
self.store.clone()
|
||||
}
|
||||
|
||||
pub async fn reload_package(&mut self, key: PkgKey) {
|
||||
let dir = util::pkg_dir().join(&key.0);
|
||||
if let Ok(pkg) = Package::from_dir(dir).await {
|
||||
self.update_package(key, pkg);
|
||||
} else {
|
||||
log::error!("couldn't reload {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reload_all(&mut self) -> Result<()> {
|
||||
let dirents = std::fs::read_dir(util::pkg_dir())?;
|
||||
let mut futures = JoinSet::new();
|
||||
|
||||
for dir in dirents {
|
||||
if let Ok(dir) = dir {
|
||||
let path = dir.path();
|
||||
futures.spawn(Package::from_dir(path));
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(res) = futures.join_next().await {
|
||||
if let Ok(Ok(pkg)) = res {
|
||||
self.update_package(pkg.key(), pkg);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fetch_listings(&mut self) -> Result<()> {
|
||||
if self.has_fetched {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use futures::{
|
||||
io::{self, BufReader, ErrorKind},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
let response = reqwest::get("https://rainy.patafour.zip/c/ongeki/api/v1/package/").await?;
|
||||
let reader = response
|
||||
.bytes_stream()
|
||||
.map_err(|e| io::Error::new(ErrorKind::Other, e))
|
||||
.into_async_read();
|
||||
|
||||
let mut decoder = GzipDecoder::new(BufReader::new(reader));
|
||||
let mut data = String::new();
|
||||
decoder.read_to_string(&mut data).await?;
|
||||
|
||||
let listings: Vec<rainy::V1Package> = serde_json::from_str(&data)
|
||||
.expect("Invalid JSON");
|
||||
|
||||
for listing in listings {
|
||||
// This is None if the package has no versions for whatever reason
|
||||
if let Some(r) = Package::from_rainy(listing) {
|
||||
//log::warn!("D {}", &r.rmt.as_ref().unwrap().dependencies.first().unwrap_or(&"Nothing".to_owned()));
|
||||
match self.store.get_mut(&r.key()) {
|
||||
Some(l) => {
|
||||
l.rmt = r.rmt;
|
||||
}
|
||||
None => {
|
||||
self.store.insert(r.key(), r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.has_fetched = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn install_package(&mut self, key: &PkgKey, force: bool) -> Result<InstallResult> {
|
||||
log::debug!("Installing {}", key);
|
||||
|
||||
let pkg = self.store.get(key)
|
||||
.ok_or_else(|| anyhow!("Attempted to install a nonexistent pkg"))?
|
||||
.clone();
|
||||
|
||||
if pkg.loc.is_some() && !force {
|
||||
return Ok(InstallResult::Ready);
|
||||
}
|
||||
let rmt = pkg.rmt.as_ref() //clone()
|
||||
.ok_or_else(|| anyhow!("Attempted to install a pkg without remote data"))?;
|
||||
|
||||
for dep in &rmt.dependencies {
|
||||
self.app.emit("install-start", Payload {
|
||||
pkg: dep.to_owned()
|
||||
})?;
|
||||
Box::pin(self.install_package(&dep, false)).await?;
|
||||
}
|
||||
|
||||
let zip_path = util::cache_dir().join(format!(
|
||||
"{}-{}-{}.zip",
|
||||
pkg.namespace, pkg.name, rmt.version
|
||||
));
|
||||
|
||||
if !zip_path.exists() {
|
||||
self.dlh.download_zip(&zip_path, &pkg)?;
|
||||
return Ok(InstallResult::Deferred);
|
||||
}
|
||||
|
||||
let cache_file_r = std::fs::File::open(&zip_path)?;
|
||||
let mut archive = zip::ZipArchive::new(cache_file_r)?;
|
||||
|
||||
self.delete_package(key, false).await?;
|
||||
|
||||
let path = pkg.path();
|
||||
fs::create_dir(&path).await?;
|
||||
archive.extract(path)?;
|
||||
self.reload_package(key.to_owned()).await;
|
||||
|
||||
self.app.emit("install-end", Payload {
|
||||
pkg: key.to_owned()
|
||||
})?;
|
||||
|
||||
log::info!("Installed {}", key);
|
||||
|
||||
Ok(InstallResult::Ready)
|
||||
}
|
||||
|
||||
pub async fn delete_package(&mut self, key: &PkgKey, force: bool) -> Result<()> {
|
||||
let pkg = self.store.get_mut(key)
|
||||
.ok_or_else(|| anyhow!("Attempted to delete a nonexistent pkg"))?;
|
||||
let path = pkg.path();
|
||||
|
||||
if path.exists() && path.join("manifest.json").exists() {
|
||||
// TODO don't rm -r - use a file whitelist
|
||||
log::debug!("rm -r'ing {}", path.to_string_lossy());
|
||||
pkg.loc = None;
|
||||
let rv = tokio::fs::remove_dir_all(&path).await
|
||||
.map_err(|e| anyhow!("Could not delete a package: {}", e));
|
||||
|
||||
if rv.is_ok() {
|
||||
self.app.emit("install-end", Payload {
|
||||
pkg: key.to_owned()
|
||||
})?;
|
||||
log::info!("Deleted {}", key);
|
||||
}
|
||||
rv
|
||||
} else {
|
||||
if force {
|
||||
Err(anyhow!("Nothing to delete"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_package(&mut self, key: PkgKey, mut new: Package) {
|
||||
if let Some(old) = self.store.get(&key) {
|
||||
new.rmt = old.rmt.clone();
|
||||
}
|
||||
self.store.insert(key, new);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user