can_viewer/
config.rs

1//! Session configuration persistence.
2//!
3//! Saves and loads user session settings like the last used DBC file.
4
5use serde::{Deserialize, Serialize};
6use std::fs;
7use std::path::PathBuf;
8
9/// Embedded sample files for first run
10const SAMPLE_MF4: &[u8] = include_bytes!("../examples/sample.mf4");
11const SAMPLE_DBC: &[u8] = include_bytes!("../examples/sample.dbc");
12
13/// Session configuration that persists across app restarts.
14#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15pub struct SessionConfig {
16    /// Path to the last loaded DBC file.
17    pub dbc_path: Option<String>,
18    /// Path to the last loaded MDF4 file.
19    pub mdf4_path: Option<String>,
20    /// Whether first-run setup has been completed.
21    #[serde(default)]
22    pub setup_complete: bool,
23}
24
25impl SessionConfig {
26    /// Get the config directory for the application.
27    pub fn config_dir() -> Option<PathBuf> {
28        dirs::config_dir().map(|p| p.join("can-viewer"))
29    }
30
31    /// Get the config file path for the application.
32    pub fn config_path() -> Option<PathBuf> {
33        Self::config_dir().map(|p| p.join("session.json"))
34    }
35
36    /// Load session config from disk, running first-time setup if needed.
37    pub fn load() -> Self {
38        // Load existing config or create default
39        let mut config: Self = Self::config_path()
40            .and_then(|path| fs::read_to_string(&path).ok())
41            .and_then(|content| serde_json::from_str(&content).ok())
42            .unwrap_or_default();
43
44        // First run: extract samples and set as defaults
45        if !config.setup_complete {
46            if let Some(config_dir) = Self::config_dir() {
47                let _ = fs::create_dir_all(&config_dir);
48
49                let mf4_path = config_dir.join("sample.mf4");
50                let dbc_path = config_dir.join("sample.dbc");
51
52                // Extract sample files
53                let _ = fs::write(&mf4_path, SAMPLE_MF4);
54                let _ = fs::write(&dbc_path, SAMPLE_DBC);
55
56                // Set as defaults
57                config.mdf4_path = Some(mf4_path.to_string_lossy().into_owned());
58                config.dbc_path = Some(dbc_path.to_string_lossy().into_owned());
59                config.setup_complete = true;
60                let _ = config.save();
61            }
62        }
63
64        config
65    }
66
67    /// Save session config to disk.
68    pub fn save(&self) -> Result<(), String> {
69        let path = Self::config_path().ok_or("Could not determine config directory")?;
70
71        // Create config directory if it doesn't exist
72        if let Some(parent) = path.parent() {
73            fs::create_dir_all(parent)
74                .map_err(|e| format!("Failed to create config directory: {}", e))?;
75        }
76
77        let content = serde_json::to_string_pretty(self)
78            .map_err(|e| format!("Failed to serialize: {}", e))?;
79
80        fs::write(&path, content).map_err(|e| format!("Failed to write config: {}", e))?;
81
82        Ok(())
83    }
84
85    /// Update DBC path and save.
86    pub fn set_dbc_path(&mut self, path: Option<String>) -> Result<(), String> {
87        self.dbc_path = path;
88        self.save()
89    }
90
91    /// Update MDF4 path and save.
92    pub fn set_mdf4_path(&mut self, path: Option<String>) -> Result<(), String> {
93        self.mdf4_path = path;
94        self.save()
95    }
96}