Files
STARTLINER/rust/src/util.rs

153 lines
4.6 KiB
Rust

use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager};
use tokio::process::Command;
use std::{path::{Path, PathBuf}, sync::OnceLock};
use crate::model::misc::Game;
#[cfg(not(target_os = "windows"))]
static NAME: &str = "startliner";
#[cfg(target_os = "windows")]
static NAME: &str = "STARTLINER";
#[derive(Clone, Serialize, Deserialize)]
pub struct Dirs {
config_dir: PathBuf,
data_dir: PathBuf,
cache_dir: PathBuf,
}
static DIRS: OnceLock<Dirs> = OnceLock::new();
pub fn init_dirs(apph: &AppHandle) {
DIRS.get_or_init(|| {
if cfg!(windows) {
Dirs {
config_dir: apph.path().data_dir().expect("Unable to set project directories").join(NAME),
data_dir: apph.path().cache_dir().expect("Unable to set project directories").join(NAME).join("data"),
cache_dir: apph.path().cache_dir().expect("Unable to set project directories").join(NAME).join("cache"),
}
} else {
Dirs {
config_dir: apph.path().config_dir().expect("Unable to set project directories").join(NAME),
data_dir: apph.path().data_dir().expect("Unable to set project directories").join(NAME),
cache_dir: apph.path().cache_dir().expect("Unable to set project directories").join(NAME),
}
}
});
}
pub fn all_dirs() -> &'static Dirs {
&DIRS.get().expect("Directories uninitialized")
}
pub fn config_dir() -> &'static Path {
&DIRS.get().expect("Directories uninitialized").config_dir
}
pub fn profile_config_dir(game: &Game, name: &str) -> PathBuf {
config_dir().join(format!("profile-{}-{}", game, name))
}
pub fn data_dir() -> &'static Path {
&DIRS.get().expect("Directories uninitialized").data_dir
}
pub fn cache_dir() -> &'static Path {
&DIRS.get().expect("Directories uninitialized").cache_dir
}
pub fn pkg_dir() -> PathBuf {
data_dir().join("pkg")
}
pub fn pkg_dir_of(namespace: &str, name: &str) -> PathBuf {
pkg_dir().join(format!("{}-{}", namespace, name))
}
pub fn copy_directory(src: impl AsRef<Path>, dst: impl AsRef<Path>, recursive: bool) -> std::io::Result<()> {
std::fs::create_dir_all(dst.as_ref()).unwrap();
for entry in std::fs::read_dir(src.as_ref())? {
let entry = entry?;
let meta = entry.metadata()?;
if meta.is_dir() {
if recursive == true {
copy_directory(&entry.path(), &dst.as_ref().join(entry.file_name()), true)?;
} else {
log::warn!("Skipping directory {:?}", meta);
}
} else {
std::fs::copy(&entry.path(), &dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
tokio::fs::symlink(src, dst).await
}
#[cfg(target_os = "windows")]
pub async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
//std::os::windows::fs::junction_point(src, dst) // is unstable
junction::create(src, dst)
}
#[cfg(target_os = "windows")]
pub static CREATE_NO_WINDOW: u32 = 0x08000000;
#[cfg(target_os = "windows")]
pub async fn pkill(process_name: &str) {
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg(process_name)
.creation_flags(CREATE_NO_WINDOW).output().await;
}
#[cfg(target_os = "linux")]
pub async fn pkill(process_name: &str) {
_ = Command::new("pkill").arg(process_name)
.output().await;
}
pub fn clean_up_opts(dir: impl AsRef<Path>) -> Result<()> {
log::debug!("begin clean_up_opts");
if dir.as_ref().is_dir() {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
log::debug!("{:?}", path);
if path.is_symlink() {
#[cfg(target_os = "windows")]
std::fs::remove_dir(path)?;
#[cfg(not(target_os = "windows"))]
std::fs::remove_file(path)?;
} else {
log::error!("Not a symlink: {:?}", path);
}
}
}
log::debug!("end clean_up_opts");
Ok(())
}
pub trait PathStr {
fn stringify(&self) -> Result<String>;
}
fn path_to_str(p: impl AsRef<Path>) -> Result<String> {
Ok(p.as_ref().to_str().ok_or_else(|| anyhow!("Invalid path: {:?}", p.as_ref()))?.to_owned())
}
impl PathStr for Path {
fn stringify(&self) -> Result<String> {
path_to_str(self)
}
}
impl PathStr for PathBuf {
fn stringify(&self) -> Result<String> {
path_to_str(&self)
}
}