feat: phase 2
Newfound motivation
This commit is contained in:
168
rust/src/pkg.rs
Normal file
168
rust/src/pkg.rs
Normal file
@ -0,0 +1,168 @@
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use derive_more::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::fs;
|
||||
use crate::{model::{local, rainy}, util};
|
||||
|
||||
// {namespace}-{name}
|
||||
#[derive(Eq, Hash, PartialEq, Clone, Serialize, Deserialize, Display)]
|
||||
pub struct PkgKey(pub String);
|
||||
|
||||
// {namespace}-{name}-{version}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct PkgKeyVersion(String);
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Package {
|
||||
pub namespace: String,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub icon: String,
|
||||
pub loc: Option<Local>,
|
||||
pub rmt: Option<Remote>
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub enum Kind {
|
||||
#[default] Mod,
|
||||
UnsupportedMod
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Local {
|
||||
pub version: String,
|
||||
pub path: PathBuf,
|
||||
pub dependencies: Vec<PkgKey>,
|
||||
pub kind: Kind
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Remote {
|
||||
pub version: String,
|
||||
pub package_url: String,
|
||||
pub download_url: String,
|
||||
pub deprecated: bool,
|
||||
pub dependencies: Vec<PkgKey>
|
||||
}
|
||||
|
||||
impl Package {
|
||||
pub fn from_rainy(mut p: rainy::V1Package) -> Option<Package> {
|
||||
if p.versions.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let v = p.versions.swap_remove(0);
|
||||
|
||||
Some(Package {
|
||||
namespace: p.owner,
|
||||
name: v.name,
|
||||
description: v.description,
|
||||
icon: v.icon,
|
||||
loc: None,
|
||||
rmt: Some(Remote {
|
||||
package_url: p.package_url,
|
||||
download_url: v.download_url,
|
||||
deprecated: p.is_deprecated,
|
||||
version: v.version_number,
|
||||
dependencies: Self::sanitize_deps(v.dependencies)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn from_dir(dir: PathBuf) -> Result<Package> {
|
||||
let str = fs::read_to_string(dir.join("manifest.json")).await?;
|
||||
let mft: local::PackageManifest = serde_json::from_str(&str)?;
|
||||
|
||||
let icon = dir.join("icon.png")
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
let dependencies = Self::sanitize_deps(mft.dependencies);
|
||||
|
||||
Ok(Package {
|
||||
namespace: Self::dir_to_namespace(&dir)?,
|
||||
name: mft.name.clone(),
|
||||
description: mft.description.clone(),
|
||||
icon,
|
||||
loc: Some(Local {
|
||||
version: mft.version_number,
|
||||
path: dir.to_owned(),
|
||||
kind: Kind::Mod,
|
||||
dependencies
|
||||
}),
|
||||
rmt: None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn key(&self) -> PkgKey {
|
||||
PkgKey(format!("{}-{}", self.namespace, self.name))
|
||||
}
|
||||
|
||||
pub fn path(&self) -> PathBuf {
|
||||
util::pkg_dir().join(self.key().0)
|
||||
}
|
||||
|
||||
pub fn _dir_to_key(dir: &Path) -> Result<String> {
|
||||
let (key, _) = Self::parse_dir_name(dir)?;
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
pub fn dir_to_namespace(dir: &Path) -> Result<String> {
|
||||
let (_, n) = Self::parse_dir_name(dir)?;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn manifest(dir: &Path) -> Result<local::PackageManifest> {
|
||||
serde_json::from_reader(std::fs::File::open(dir.join("manifest.json"))?)
|
||||
.map_err(|err| anyhow!("Invalid manifest: {}", err))
|
||||
}
|
||||
|
||||
fn parse_dir_name(dir: &Path) -> Result<(String, String)> {
|
||||
let mft = Self::manifest(dir)?;
|
||||
let regex = regex::Regex::new(r"([A-Za-z0-9_]+)-([A-Za-z0-9_]+)$")?;
|
||||
let dir_name = dir.file_name()
|
||||
.to_owned()
|
||||
.ok_or_else(|| anyhow!("Invalid directory name"))?
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("Illegal directory name"))?;
|
||||
|
||||
let namespace;
|
||||
|
||||
if let Some(caps) = regex.captures(dir_name) {
|
||||
let name_match = caps.get(2)
|
||||
.ok_or_else(|| anyhow!("Invalid directory name"))?;
|
||||
|
||||
if name_match.as_str() != mft.name {
|
||||
bail!("Invalid manifest or directory name");
|
||||
}
|
||||
|
||||
namespace = caps.get(1)
|
||||
.ok_or_else(|| anyhow!("Invalid directory name?"))?
|
||||
.as_str()
|
||||
.to_owned();
|
||||
|
||||
Ok((format!("{}-{}", namespace, mft.name), namespace))
|
||||
} else {
|
||||
bail!("Error reading {}: invalid directory name", dir_name);
|
||||
}
|
||||
}
|
||||
|
||||
fn sanitize_deps(mut deps: Vec<PkgKeyVersion>) -> Vec<PkgKey> {
|
||||
let regex = regex::Regex::new(r"([A-Za-z0-9_]+)-([A-Za-z0-9_]+)-[0-9\.]+$")
|
||||
.expect("Invalid regex");
|
||||
|
||||
for i in 0..deps.len() {
|
||||
let caps = regex.captures(&deps[i].0)
|
||||
.expect("Invalid dependency");
|
||||
deps[i] = PkgKeyVersion(format!("{}-{}", caps.get(1).unwrap().as_str(), caps.get(2).unwrap().as_str()));
|
||||
}
|
||||
let rv: Vec<PkgKey> = unsafe { std::mem::transmute(deps) };
|
||||
rv
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user