devalang_wasm/tools/cli/config/
user.rs

1#![cfg(feature = "cli")]
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::fs;
6use std::io::Write;
7use std::path::PathBuf;
8
9/// Devalang user configuration
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct UserConfig {
12    /// Session token for authentication
13    pub session: String,
14    /// Telemetry configuration
15    #[serde(default)]
16    pub telemetry: TelemetryConfig,
17}
18
19/// Telemetry configuration
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct TelemetryConfig {
22    /// User's unique UUID
23    pub uuid: String,
24    /// Telemetry enabled or not
25    pub enabled: bool,
26    /// Telemetry level
27    pub level: String,
28    /// Statistics enabled or not
29    pub stats: bool,
30}
31
32impl Default for UserConfig {
33    fn default() -> Self {
34        Self {
35            session: String::new(),
36            telemetry: TelemetryConfig::default(),
37        }
38    }
39}
40
41impl Default for TelemetryConfig {
42    fn default() -> Self {
43        Self {
44            uuid: uuid::Uuid::new_v4().to_string(),
45            enabled: false,
46            level: "basic".to_string(),
47            stats: false,
48        }
49    }
50}
51
52/// Gets the user's home directory
53pub fn get_home_dir() -> Option<PathBuf> {
54    dirs::home_dir()
55}
56
57/// Gets the Devalang configuration directory
58pub fn get_devalang_homedir() -> PathBuf {
59    if let Some(home_dir) = get_home_dir() {
60        home_dir.join(".devalang")
61    } else {
62        PathBuf::from("~/.devalang")
63    }
64}
65
66/// Gets the path to the user configuration file
67pub fn get_user_config_path() -> PathBuf {
68    get_devalang_homedir().join("config.json")
69}
70
71/// Loads the user configuration
72pub fn get_user_config() -> Option<UserConfig> {
73    let config_path = get_user_config_path();
74
75    if !config_path.exists() {
76        return None;
77    }
78
79    let file = fs::File::open(config_path).ok()?;
80    serde_json::from_reader(file).ok()
81}
82
83/// Saves the user configuration
84pub fn write_user_config(config: &UserConfig) -> Result<()> {
85    let config_path = get_user_config_path();
86
87    // Create parent directory if necessary
88    if let Some(parent) = config_path.parent() {
89        fs::create_dir_all(parent)?;
90    }
91
92    // Atomic write via temporary file
93    let tmp_path = config_path.with_extension("json.tmp");
94    let config_json = serde_json::to_string_pretty(config)?;
95
96    let mut tmp_file = fs::File::create(&tmp_path)?;
97    tmp_file.write_all(config_json.as_bytes())?;
98    tmp_file.sync_all()?;
99
100    fs::rename(&tmp_path, config_path)?;
101
102    Ok(())
103}
104
105/// Ensures the configuration file exists
106pub fn ensure_user_config_exists() -> Result<()> {
107    let config_path = get_user_config_path();
108
109    if !config_path.exists() {
110        let config = UserConfig::default();
111        write_user_config(&config)?;
112    }
113
114    Ok(())
115}
116
117/// Updates the session token
118pub fn set_session_token(token: String) -> Result<()> {
119    let mut config = get_user_config().unwrap_or_default();
120    config.session = token;
121    write_user_config(&config)?;
122    Ok(())
123}
124
125/// Gets the session token
126pub fn get_session_token() -> Option<String> {
127    get_user_config().and_then(|cfg| {
128        let token = cfg.session.trim();
129        if token.is_empty() {
130            None
131        } else {
132            Some(token.to_string())
133        }
134    })
135}
136
137/// Removes the session token (logout)
138pub fn clear_session_token() -> Result<()> {
139    let mut config = get_user_config().unwrap_or_default();
140    config.session = String::new();
141    write_user_config(&config)?;
142    Ok(())
143}