Skip to content

Commit

Permalink
configurable config,cache,data dirs
Browse files Browse the repository at this point in the history
  • Loading branch information
Buckminsterfullerene02 committed Jan 20, 2024
1 parent d91b633 commit 609b044
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 46 deletions.
159 changes: 121 additions & 38 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ mod toggle_switch;

use std::collections::{BTreeMap, BTreeSet};
use std::time::{Duration, SystemTime};
use std::{
collections::{HashMap, HashSet},
ops::DerefMut,
path::PathBuf,
};
use std::{collections::{HashMap, HashSet}, fs, ops::DerefMut, path::PathBuf};
use std::path::Path;

use anyhow::{anyhow, Context, Result};
use eframe::egui::{Button, CollapsingHeader, RichText, Visuals};
Expand All @@ -34,6 +31,9 @@ use crate::Dirs;
use crate::{
integrate::uninstall,
is_drg_pak,
is_valid_directory,
copy_directory_contents,
clear_directory,
providers::{
ApprovalStatus, FetchProgress, ModInfo, ModSpecification, ModStore, ModioTags,
ProviderFactory, RequiredStatus,
Expand Down Expand Up @@ -928,26 +928,28 @@ impl App {
});
ui.end_row();

let config_dir = &self.state.dirs.config_dir;
ui.label("Config directory:");
if ui.link(config_dir.display().to_string()).clicked() {
opener::open(config_dir).ok();
}
ui.end_row();
let edit_directory_field = |ui: &mut egui::Ui, label: &str, path: &mut String, err: &mut Option<String>| {
ui.horizontal(|ui| {
ui.label(label);
ui.text_edit_singleline(path);

let cache_dir = &self.state.dirs.cache_dir;
ui.label("Cache directory:");
if ui.link(cache_dir.display().to_string()).clicked() {
opener::open(cache_dir).ok();
}
ui.end_row();
if ui.button("Browse").clicked() {
if let Some(selected_path) = rfd::FileDialog::new().pick_folder() {
*path = selected_path.to_string_lossy().to_string();
*err = None;
}
}

let data_dir = &self.state.dirs.data_dir;
ui.label("Data directory:");
if ui.link(data_dir.display().to_string()).clicked() {
opener::open(data_dir).ok();
}
ui.end_row();
if let Some(err_msg) = err {
ui.label(&*err_msg);
}
ui.end_row();
});
};

edit_directory_field(ui, "Config Directory:", &mut window.config_dir, &mut window.config_dir_err);
edit_directory_field(ui, "Cache Directory:", &mut window.cache_dir, &mut window.cache_dir_err);
edit_directory_field(ui, "Data Directory:", &mut window.data_dir, &mut window.data_dir_err);

ui.label("GUI theme:");
ui.horizontal(|ui| {
Expand Down Expand Up @@ -980,24 +982,91 @@ impl App {
}
});

ui.with_layout(egui::Layout::right_to_left(Align::TOP), |ui| {
if ui.add_enabled(window.drg_pak_path_err.is_none(), egui::Button::new("save")).clicked() {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
let can_save = window.drg_pak_path_err.is_none()
&& window.config_dir_err.is_none()
&& window.cache_dir_err.is_none()
&& window.data_dir_err.is_none();

if ui.add_enabled(can_save, egui::Button::new("save")).clicked() {
try_save = true;
}

if let Some(error) = &window.drg_pak_path_err {
ui.colored_label(ui.visuals().error_fg_color, error);
}
if let Some(error) = &window.config_dir_err {
ui.colored_label(ui.visuals().error_fg_color, error);
}
if let Some(error) = &window.cache_dir_err {
ui.colored_label(ui.visuals().error_fg_color, error);
}
if let Some(error) = &window.data_dir_err {
ui.colored_label(ui.visuals().error_fg_color, error);
}
});

});
if try_save {
if let Err(e) = is_drg_pak(&window.drg_pak_path).context("Is not valid DRG pak") {
let mut has_error = false;

if let Err(e) = is_drg_pak(&window.drg_pak_path) {
window.drg_pak_path_err = Some(e.to_string());
} else {
self.state.config.drg_pak_path = Some(PathBuf::from(
self.settings_window.take().unwrap().drg_pak_path,
));
self.state.config.save().unwrap();
has_error = true;
}
if let Err(e) = is_valid_directory(&window.config_dir) {
window.config_dir_err = Some(e);
has_error = true;
}
if let Err(e) = is_valid_directory(&window.cache_dir) {
window.cache_dir_err = Some(e);
has_error = true;
}
if let Err(e) = is_valid_directory(&window.data_dir) {
window.data_dir_err = Some(e);
has_error = true;
}

if !has_error {
if let Some(old_config_dir) = &self.state.config.config_dir {
if old_config_dir.to_string_lossy() != window.config_dir {
if let Err(e) = copy_directory_contents(old_config_dir, Path::new(&window.config_dir)) {
window.config_dir_err = Some(format!("Failed to copy config directory: {}", e));
has_error = true;
}
}
}

if let Some(old_data_dir) = &self.state.config.data_dir {
if old_data_dir.to_string_lossy() != window.data_dir {
if let Err(e) = copy_directory_contents(old_data_dir, Path::new(&window.data_dir)) {
window.data_dir_err = Some(format!("Failed to copy data directory: {}", e));
has_error = true;
}
}
}

if let Some(old_cache_dir) = &self.state.config.cache_dir {
if old_cache_dir.to_string_lossy() != window.cache_dir {
if let Err(e) = fs::create_dir_all(&window.cache_dir) {
window.cache_dir_err = Some(format!("Failed to create new cache directory: {}", e));
has_error = true;
}
else if let Err(e) = clear_directory(old_cache_dir) {
window.cache_dir_err = Some(format!("Failed to clear cache directory: {}", e));
has_error = true;
}
}
}

if !has_error {
self.state.config.drg_pak_path = Some(PathBuf::from(&window.drg_pak_path));
self.state.config.config_dir = Some(PathBuf::from(&window.config_dir));
self.state.config.cache_dir = Some(PathBuf::from(&window.cache_dir));
self.state.config.data_dir = Some(PathBuf::from(&window.data_dir));

self.state.config.save().unwrap();
}
}
} else if !open {
self.settings_window = None;
Expand Down Expand Up @@ -1483,19 +1552,33 @@ impl WindowProviderParameters {
struct WindowSettings {
drg_pak_path: String,
drg_pak_path_err: Option<String>,
config_dir: String,
config_dir_err: Option<String>,
cache_dir: String,
cache_dir_err: Option<String>,
data_dir: String,
data_dir_err: Option<String>,
}

impl WindowSettings {
fn new(state: &State) -> Self {
let path = state
.config
.drg_pak_path
.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default();
Self {
drg_pak_path: path,
drg_pak_path: state.config.drg_pak_path.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default(),
drg_pak_path_err: None,
config_dir: state.config.config_dir.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default(),
config_dir_err: None,
cache_dir: state.config.cache_dir.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default(),
cache_dir_err: None,
data_dir: state.config.data_dir.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default(),
data_dir_err: None,
}
}
}
Expand Down
67 changes: 63 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ pub mod mod_lints;
pub mod providers;
pub mod state;

use std::io::{Cursor, Read};
use std::io::{self, Cursor, Read};
use std::str::FromStr;
use std::{
collections::HashSet,
path::{Path, PathBuf},
use std::{collections::HashSet, path::{Path, PathBuf}};
use std::fs::{
self,
copy,
create_dir_all,
};

use anyhow::{Context, Result};
Expand Down Expand Up @@ -87,6 +89,63 @@ pub fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, data: C) -> Result<()
.with_context(|| format!("Could not write to file {}", path.as_ref().display()))
}

pub fn is_valid_directory(path: &str) -> Result<(), String> {
let path = Path::new(path);

if !path.exists() {
return Err("Path does not exist.".to_string());
}
if !path.is_dir() {
return Err("Path is not a directory.".to_string());
}

match fs::metadata(path) {
Ok(metadata) => {
if !metadata.permissions().readonly() {
Ok(())
} else {
Err("Directory is not writable.".to_string())
}
}
Err(_) => Err("Unable to access directory metadata.".to_string()),
}
}

pub fn copy_directory_contents(src: &Path, dest: &Path) -> io::Result<()> {
if src.is_dir() {
create_dir_all(dest)?;

for entry in fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
let dest_path = dest.join(entry.file_name());

if path.is_dir() {
copy_directory_contents(&path, &dest_path)?;
} else {
copy(&path, &dest_path)?;
}
}
}
Ok(())
}

pub fn clear_directory(path: &Path) -> io::Result<()> {
if path.is_dir() {
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();

if path.is_dir() {
fs::remove_dir_all(&path)?;
} else {
fs::remove_file(&path)?;
}
}
}
Ok(())
}

pub fn is_drg_pak<P: AsRef<Path>>(path: P) -> Result<()> {
let mut reader = std::io::BufReader::new(open_file(path)?);
let pak = repak::PakBuilder::new().reader(&mut reader)?;
Expand Down
Loading

0 comments on commit 609b044

Please sign in to comment.