feat: use sets etc.

This commit is contained in:
2025-02-27 16:42:43 +00:00
parent 3d96d89846
commit d25841853c
11 changed files with 587 additions and 563 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,50 +1,50 @@
{ {
"name": "startliner", "name": "startliner",
"private": true, "private": true,
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
"preview": "vite preview", "preview": "vite preview",
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@mdi/font": "7.4.47", "@mdi/font": "7.4.47",
"@primevue/forms": "^4.2.5", "@primevue/forms": "^4.3.1",
"@primevue/themes": "^4.2.5", "@primevue/themes": "^4.3.1",
"@tailwindcss/vite": "^4.0.6", "@tailwindcss/vite": "^4.0.9",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2.3.0",
"@tauri-apps/plugin-deep-link": "~2", "@tauri-apps/plugin-deep-link": "~2.2.0",
"@tauri-apps/plugin-dialog": "~2", "@tauri-apps/plugin-dialog": "~2.2.0",
"@tauri-apps/plugin-fs": "~2", "@tauri-apps/plugin-fs": "~2.2.0",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2.2.5",
"@tauri-apps/plugin-shell": "~2", "@tauri-apps/plugin-shell": "~2.2.0",
"@trivago/prettier-plugin-sort-imports": "^5.2.2", "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"pinia": "^3.0.1", "pinia": "^3.0.1",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^4.2.5", "primevue": "^4.3.1",
"roboto-fontface": "*", "roboto-fontface": "^0.10.0",
"tailwindcss": "^4.0.6", "tailwindcss": "^4.0.9",
"tailwindcss-primeui": "^0.4.0", "tailwindcss-primeui": "^0.4.0",
"vue": "^3.5.13", "vue": "^3.5.13",
"vuetify": "^3.7.6" "vuetify": "^3.7.14"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2", "@tauri-apps/cli": "^2.3.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"sass-embedded": "^1.83.1", "sass-embedded": "^1.85.1",
"typescript": "^5.8.0-dev.20250209", "typescript": "^5.8.0-dev.20250218",
"vite": "^6.0.3", "vite": "^6.2.0",
"vue-tsc": "^2.1.10", "vue-tsc": "^2.2.4",
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",
"@types/node": "^22.9.0", "@types/node": "^22.13.5",
"@vue/eslint-config-typescript": "^14.1.3", "@vue/eslint-config-typescript": "^14.4.0",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"npm-run-all2": "^7.0.1", "npm-run-all2": "^7.0.2",
"sass": "1.77.8", "sass": "1.77.8",
"unplugin-fonts": "^1.1.1", "unplugin-fonts": "^1.3.1",
"unplugin-vue-components": "^0.27.2", "unplugin-vue-components": "^0.27.5",
"vite-plugin-vuetify": "^2.0.3" "vite-plugin-vuetify": "^2.1.0"
} }
} }

941
rust/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -147,16 +147,50 @@ pub async fn init_profile(
Ok(new_profile) Ok(new_profile)
} }
// #[tauri::command]
// pub async fn profile_dir(
// state: State<'_, Mutex<AppData>>
// ) -> Result<PathBuf, &str> {
// let appd = state.lock().await;
// if let Some(p) = &appd.profile {
// Ok(p.dir())
// } else {
// Err("No profile loaded")
// }
// }
// the tauri fs plugin doesn't fucking work
#[tauri::command] #[tauri::command]
pub async fn profile_dir( pub async fn read_profile_data(
state: State<'_, Mutex<AppData>> state: State<'_, Mutex<AppData>>,
) -> Result<PathBuf, &str> { path: PathBuf
) -> Result<String, String> {
let appd = state.lock().await; let appd = state.lock().await;
if let Some(p) = &appd.profile { if let Some(p) = &appd.profile {
Ok(p.dir()) let res = fs::read_to_string(p.dir().join(&path)).await
.map_err(|e| format!("Unable to open {:?}: {}", path, e))?;
Ok(res)
} else { } else {
Err("No profile loaded") Err("No profile loaded".to_owned())
}
}
#[tauri::command]
pub async fn write_profile_data(
state: State<'_, Mutex<AppData>>,
path: PathBuf,
content: String
) -> Result<(), String> {
let appd = state.lock().await;
if let Some(p) = &appd.profile {
fs::write(p.dir().join(&path), content).await
.map_err(|e| format!("Unable to write to {:?}: {}", path, e))?;
Ok(())
} else {
Err("No profile loaded".to_owned())
} }
} }

View File

@ -106,7 +106,8 @@ pub async fn run(_args: Vec<String>) {
cmd::get_current_profile, cmd::get_current_profile,
cmd::init_profile, cmd::init_profile,
cmd::save_profile, cmd::save_profile,
cmd::profile_dir, cmd::read_profile_data,
cmd::write_profile_data,
cmd::startline, cmd::startline,
cmd::kill, cmd::kill,
cmd::set_cfg, cmd::set_cfg,

View File

@ -1,4 +1,3 @@
use tokio::task::JoinSet;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use tokio::fs; use tokio::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -18,6 +17,14 @@ async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Resul
} }
pub async fn line_up(p: &Profile, pkg_hash: String) -> Result<()> { pub async fn line_up(p: &Profile, pkg_hash: String) -> Result<()> {
let dir_out = p.dir();
if dir_out.join("option").exists() {
fs::remove_dir_all(dir_out.join("option")).await?;
}
fs::create_dir_all(dir_out.join("option")).await?;
let hash_path = p.dir().join(".sl-state"); let hash_path = p.dir().join(".sl-state");
let prev_hash = fs::read_to_string(&hash_path).await.unwrap_or_default(); let prev_hash = fs::read_to_string(&hash_path).await.unwrap_or_default();
if prev_hash != pkg_hash { if prev_hash != pkg_hash {
@ -35,21 +42,14 @@ pub async fn line_up(p: &Profile, pkg_hash: String) -> Result<()> {
async fn prepare_packages(p: &Profile) -> Result<()> { async fn prepare_packages(p: &Profile) -> Result<()> {
let dir_out = p.dir(); let dir_out = p.dir();
let mut futures = JoinSet::new();
if dir_out.join("BepInEx").exists() { if dir_out.join("BepInEx").exists() {
futures.spawn(fs::remove_dir_all(dir_out.join("BepInEx"))); fs::remove_dir_all(dir_out.join("BepInEx")).await?;
} }
if dir_out.join("option").exists() {
futures.spawn(fs::remove_dir_all(dir_out.join("option")));
}
while let Some(_) = futures.join_next().await {}
fs::create_dir_all(dir_out.join("option")).await?;
for m in &p.mods { for m in &p.mods {
log::debug!("Preparing {}", m); log::debug!("Preparing {}", m);
let (namespace, name) = m.0.split_at(m.0.find("-").expect("Invalid mod definition")); let (namespace, name) = m.0.split_at(m.0.find("-").expect("Invalid mod definition"));
let bpx_dir = util::pkg_dir_of(namespace, &name[1..]) let bpx_dir = util::pkg_dir_of(namespace, &name[1..]) // cut the hyphen
.join("app") .join("app")
.join("BepInEx"); .join("BepInEx");
if bpx_dir.exists() { if bpx_dir.exists() {

View File

@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use serde::Deserialize; use serde::Deserialize;
use crate::pkg::PkgKeyVersion; use crate::pkg::PkgKeyVersion;
@ -9,5 +10,5 @@ pub struct PackageManifest {
pub name: String, pub name: String,
pub version_number: String, pub version_number: String,
pub description: String, pub description: String,
pub dependencies: Vec<PkgKeyVersion> pub dependencies: BTreeSet<PkgKeyVersion>
} }

View File

@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use serde::Deserialize; use serde::Deserialize;
use crate::pkg::PkgKeyVersion; use crate::pkg::PkgKeyVersion;
@ -19,6 +20,6 @@ pub struct V1Version {
pub description: String, pub description: String,
pub version_number: String, pub version_number: String,
pub icon: String, pub icon: String,
pub dependencies: Vec<PkgKeyVersion>, pub dependencies: BTreeSet<PkgKeyVersion>,
pub download_url: String, pub download_url: String,
} }

View File

@ -1,7 +1,7 @@
use anyhow::{Result, anyhow, bail}; use anyhow::{Result, anyhow, bail};
use derive_more::Display; use derive_more::Display;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::{collections::BTreeSet, path::{Path, PathBuf}};
use tokio::fs; use tokio::fs;
use crate::{model::{local, rainy}, util}; use crate::{model::{local, rainy}, util};
@ -10,7 +10,7 @@ use crate::{model::{local, rainy}, util};
pub struct PkgKey(pub String); pub struct PkgKey(pub String);
// {namespace}-{name}-{version} // {namespace}-{name}-{version}
#[derive(Clone, Serialize, Deserialize)] #[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Display)]
pub struct PkgKeyVersion(String); pub struct PkgKeyVersion(String);
#[derive(Clone, Default, Serialize, Deserialize)] #[derive(Clone, Default, Serialize, Deserialize)]
@ -36,7 +36,7 @@ pub enum Kind {
pub struct Local { pub struct Local {
pub version: String, pub version: String,
pub path: PathBuf, pub path: PathBuf,
pub dependencies: Vec<PkgKey>, pub dependencies: BTreeSet<PkgKey>,
pub kind: Kind pub kind: Kind
} }
@ -47,7 +47,7 @@ pub struct Remote {
pub package_url: String, pub package_url: String,
pub download_url: String, pub download_url: String,
pub deprecated: bool, pub deprecated: bool,
pub dependencies: Vec<PkgKey> pub dependencies: BTreeSet<PkgKey>
} }
impl Package { impl Package {
@ -154,16 +154,16 @@ impl Package {
} }
} }
fn sanitize_deps(mut deps: Vec<PkgKeyVersion>) -> Vec<PkgKey> { fn sanitize_deps(src: BTreeSet<PkgKeyVersion>) -> BTreeSet<PkgKey> {
let regex = regex::Regex::new(r"([A-Za-z0-9_]+)-([A-Za-z0-9_]+)-[0-9\.]+$") let regex = regex::Regex::new(r"([A-Za-z0-9_]+)-([A-Za-z0-9_]+)-[0-9\.]+$")
.expect("Invalid regex"); .expect("Invalid regex");
let mut res = BTreeSet::<PkgKey>::new();
for i in 0..deps.len() { for dep in src {
let caps = regex.captures(&deps[i].0) let caps = regex.captures(&dep.0)
.expect("Invalid dependency"); .expect("Invalid dependency");
deps[i] = PkgKeyVersion(format!("{}-{}", caps.get(1).unwrap().as_str(), caps.get(2).unwrap().as_str())); res.insert(PkgKey(format!("{}-{}", caps.get(1).unwrap().as_str(), caps.get(2).unwrap().as_str())));
} }
let rv: Vec<PkgKey> = unsafe { std::mem::transmute(deps) }; res
rv
} }
} }

View File

@ -73,6 +73,7 @@ impl Profile {
log::info!("Written to {}", path.to_string_lossy()); log::info!("Written to {}", path.to_string_lossy());
} }
#[allow(dead_code)]
pub fn get_cfg(&self, key: &str) -> Result<&serde_json::Value> { pub fn get_cfg(&self, key: &str) -> Result<&serde_json::Value> {
self.cfg.get(key) self.cfg.get(key)
.ok_or_else(|| anyhow::anyhow!("Invalid config entry {}", key)) .ok_or_else(|| anyhow::anyhow!("Invalid config entry {}", key))

View File

@ -7,7 +7,7 @@ import RadioButton from 'primevue/radiobutton';
import Toggle from 'primevue/toggleswitch'; import Toggle from 'primevue/toggleswitch';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import * as path from '@tauri-apps/api/path'; import * as path from '@tauri-apps/api/path';
import { mkdir, readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'; import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
import { usePkgStore } from '../stores'; import { usePkgStore } from '../stores';
const store = usePkgStore(); const store = usePkgStore();
@ -28,16 +28,13 @@ const cfgDisplayMode = _cfg('display-mode', 'borderless');
const cfgAime = _cfg('aime', false); const cfgAime = _cfg('aime', false);
const aimeCode = ref(''); const aimeCode = ref('');
// temp
let profilePath = '';
let aimePath = '';
(async () => { (async () => {
profilePath = await invoke('profile_dir');
try { try {
aimePath = await path.join(profilePath, 'aime.txt'); aimeCode.value = await invoke('read_profile_data', {
aimeCode.value = await readTextFile(aimePath); path: 'aime.txt',
} catch (_) { });
} catch (e) {
aimeCode.value = ''; aimeCode.value = '';
} }
})(); })();
@ -49,8 +46,10 @@ const aimeCodeModel = computed({
async set(value: string) { async set(value: string) {
aimeCode.value = value; aimeCode.value = value;
if (value.match(/^[0-9]{20}$/)) { if (value.match(/^[0-9]{20}$/)) {
await mkdir(profilePath); await invoke('write_profile_data', {
await writeTextFile(aimePath, aimeCode.value); path: 'aime.txt',
content: aimeCode.value,
});
} }
}, },
}); });