Add: windows mvp - transparent bugs not fixed
This commit is contained in:
18
crates/sprimo-config/Cargo.toml
Normal file
18
crates/sprimo-config/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "sprimo-config"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
directories.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
toml.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.18.0"
|
||||
206
crates/sprimo-config/src/lib.rs
Normal file
206
crates/sprimo-config/src/lib.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use directories::ProjectDirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("no supported configuration directory for this platform")]
|
||||
MissingProjectDir,
|
||||
#[error("io error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("invalid config file: {0}")]
|
||||
Parse(#[from] toml::de::Error),
|
||||
#[error("could not encode config: {0}")]
|
||||
Encode(#[from] toml::ser::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct AppConfig {
|
||||
pub window: WindowConfig,
|
||||
pub animation: AnimationConfig,
|
||||
pub sprite: SpriteConfig,
|
||||
pub api: ApiConfig,
|
||||
pub logging: LoggingConfig,
|
||||
pub controls: ControlsConfig,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
window: WindowConfig::default(),
|
||||
animation: AnimationConfig::default(),
|
||||
sprite: SpriteConfig::default(),
|
||||
api: ApiConfig::default(),
|
||||
logging: LoggingConfig::default(),
|
||||
controls: ControlsConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct WindowConfig {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub monitor_id: Option<String>,
|
||||
pub scale: f32,
|
||||
pub always_on_top: bool,
|
||||
pub click_through: bool,
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
impl Default for WindowConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: 200.0,
|
||||
y: 200.0,
|
||||
monitor_id: None,
|
||||
scale: 1.0,
|
||||
always_on_top: true,
|
||||
click_through: false,
|
||||
visible: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct AnimationConfig {
|
||||
pub fps: u16,
|
||||
pub idle_timeout_ms: u64,
|
||||
}
|
||||
|
||||
impl Default for AnimationConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fps: 30,
|
||||
idle_timeout_ms: 3_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct SpriteConfig {
|
||||
pub selected_pack: String,
|
||||
pub sprite_packs_dir: String,
|
||||
}
|
||||
|
||||
impl Default for SpriteConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
selected_pack: "default".to_string(),
|
||||
sprite_packs_dir: "sprite-packs".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ApiConfig {
|
||||
pub port: u16,
|
||||
pub auth_token: String,
|
||||
}
|
||||
|
||||
impl Default for ApiConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
port: 32_145,
|
||||
auth_token: Uuid::new_v4().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct LoggingConfig {
|
||||
pub level: String,
|
||||
}
|
||||
|
||||
impl Default for LoggingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
level: "info".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ControlsConfig {
|
||||
pub hotkey_enabled: bool,
|
||||
pub recovery_hotkey: String,
|
||||
}
|
||||
|
||||
impl Default for ControlsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hotkey_enabled: true,
|
||||
recovery_hotkey: "Ctrl+Alt+P".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn config_path(app_name: &str) -> Result<PathBuf, ConfigError> {
|
||||
let dirs =
|
||||
ProjectDirs::from("", "", app_name).ok_or(ConfigError::MissingProjectDir)?;
|
||||
Ok(dirs.config_dir().join("config.toml"))
|
||||
}
|
||||
|
||||
pub fn load_or_create(app_name: &str) -> Result<(PathBuf, AppConfig), ConfigError> {
|
||||
let path = config_path(app_name)?;
|
||||
load_or_create_at(&path)
|
||||
}
|
||||
|
||||
pub fn load_or_create_at(path: &Path) -> Result<(PathBuf, AppConfig), ConfigError> {
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
if !path.exists() {
|
||||
let cfg = AppConfig::default();
|
||||
save(path, &cfg)?;
|
||||
return Ok((path.to_path_buf(), cfg));
|
||||
}
|
||||
|
||||
let raw = fs::read_to_string(path)?;
|
||||
let cfg = toml::from_str::<AppConfig>(&raw)?;
|
||||
Ok((path.to_path_buf(), cfg))
|
||||
}
|
||||
|
||||
pub fn save(path: &Path, config: &AppConfig) -> Result<(), ConfigError> {
|
||||
let encoded = toml::to_string_pretty(config)?;
|
||||
fs::write(path, encoded)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{load_or_create_at, save, AppConfig};
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn bootstrap_writes_default_config() {
|
||||
let temp = TempDir::new().expect("tempdir");
|
||||
let path = temp.path().join("config.toml");
|
||||
let (_, config) = load_or_create_at(&path).expect("load or create");
|
||||
assert_eq!(config.api.port, 32_145);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_roundtrip() {
|
||||
let temp = TempDir::new().expect("tempdir");
|
||||
let path = temp.path().join("config.toml");
|
||||
let mut config = AppConfig::default();
|
||||
config.window.x = 42.0;
|
||||
|
||||
save(&path, &config).expect("save");
|
||||
let (_, loaded) = load_or_create_at(&path).expect("reload");
|
||||
assert!((loaded.window.x - 42.0).abs() < f32::EPSILON);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user