use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
const RECENT_CAP: usize = 12;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CraftCharacterPrefs {
#[serde(default)]
pub favorites: Vec<String>,
#[serde(default)]
pub recent: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct CraftPrefsFile {
#[serde(default)]
pub by_character: HashMap<String, CraftCharacterPrefs>,
}
fn prefs_path() -> anyhow::Result<PathBuf> {
let home = dirs::home_dir().ok_or_else(|| anyhow::anyhow!("home directory not found"))?;
Ok(home.join(".flatland3").join("craft-prefs.json"))
}
fn load_file() -> CraftPrefsFile {
let Ok(path) = prefs_path() else {
return CraftPrefsFile::default();
};
let Ok(bytes) = std::fs::read(&path) else {
return CraftPrefsFile::default();
};
serde_json::from_slice(&bytes).unwrap_or_default()
}
fn save_file(file: &CraftPrefsFile) -> anyhow::Result<()> {
let path = prefs_path()?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, serde_json::to_vec_pretty(file)?)?;
Ok(())
}
pub fn load_for_character(character_id: &str) -> CraftCharacterPrefs {
if character_id.is_empty() {
return CraftCharacterPrefs::default();
}
load_file()
.by_character
.get(character_id)
.cloned()
.unwrap_or_default()
}
pub fn save_for_character(character_id: &str, prefs: &CraftCharacterPrefs) {
if character_id.is_empty() {
return;
}
let mut file = load_file();
file.by_character
.insert(character_id.to_string(), prefs.clone());
let _ = save_file(&file);
}
impl CraftCharacterPrefs {
pub fn is_favorite(&self, blueprint_id: &str) -> bool {
self.favorites.iter().any(|id| id == blueprint_id)
}
pub fn toggle_favorite(&mut self, blueprint_id: &str) -> bool {
if let Some(pos) = self.favorites.iter().position(|id| id == blueprint_id) {
self.favorites.remove(pos);
false
} else {
self.favorites.push(blueprint_id.to_string());
true
}
}
pub fn record_crafted(&mut self, blueprint_id: &str) {
self.recent.retain(|id| id != blueprint_id);
self.recent.insert(0, blueprint_id.to_string());
if self.recent.len() > RECENT_CAP {
self.recent.truncate(RECENT_CAP);
}
}
}