Skip to main content

wasm4pm_cli/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7#[derive(Serialize, Deserialize, Default, Debug)]
8pub struct Config {
9    #[serde(default)]
10    pub values: HashMap<String, String>,
11}
12
13impl Config {
14    /// Loads configuration from '.wasm4pm/config.json' (local) or '~/.wasm4pm/config.json' (global).
15    /// Returns a default Config if no file is found.
16    pub fn load() -> Result<Self> {
17        // Try local .wasm4pm/config.json first
18        let local_path = Path::new(".wasm4pm/config.json");
19        if local_path.exists() {
20            let content = fs::read_to_string(local_path)
21                .with_context(|| format!("Failed to read local config at {:?}", local_path))?;
22            return serde_json::from_str(&content)
23                .with_context(|| format!("Failed to parse local config at {:?}", local_path));
24        }
25
26        // Try global ~/.wasm4pm/config.json
27        if let Some(global_path) = Self::global_config_path() {
28            if global_path.exists() {
29                let content = fs::read_to_string(&global_path).with_context(|| {
30                    format!("Failed to read global config at {:?}", global_path)
31                })?;
32                return serde_json::from_str(&content).with_context(|| {
33                    format!("Failed to parse global config at {:?}", global_path)
34                });
35            }
36        }
37
38        Ok(Config::default())
39    }
40
41    /// Saves the configuration.
42    /// Prefers the local path if '.wasm4pm/' directory exists, otherwise saves to the global path.
43    pub fn save(&self) -> Result<()> {
44        let content =
45            serde_json::to_string_pretty(self).context("Failed to serialize configuration")?;
46
47        // Prefer local if .wasm4pm/ directory exists
48        let local_dir = Path::new(".wasm4pm");
49        if local_dir.exists() && local_dir.is_dir() {
50            let local_path = local_dir.join("config.json");
51            fs::write(&local_path, content)
52                .with_context(|| format!("Failed to write local config to {:?}", local_path))?;
53            return Ok(());
54        }
55
56        // Otherwise save to global
57        if let Some(global_path) = Self::global_config_path() {
58            if let Some(parent) = global_path.parent() {
59                fs::create_dir_all(parent)
60                    .with_context(|| format!("Failed to create config directory {:?}", parent))?;
61            }
62            fs::write(&global_path, content)
63                .with_context(|| format!("Failed to write global config to {:?}", global_path))?;
64            return Ok(());
65        }
66
67        anyhow::bail!(
68            "Could not determine a valid path to save configuration. (Home directory not found)"
69        )
70    }
71
72    /// Returns the global configuration path (~/.wasm4pm/config.json)
73    fn global_config_path() -> Option<PathBuf> {
74        let home = if cfg!(windows) {
75            std::env::var("USERPROFILE").ok()
76        } else {
77            std::env::var("HOME").ok()
78        };
79
80        home.map(|h| PathBuf::from(h).join(".wasm4pm").join("config.json"))
81    }
82
83    /// Gets a value from the configuration
84    pub fn get(&self, key: &str) -> Option<&String> {
85        self.values.get(key)
86    }
87
88    /// Sets a value in the configuration
89    pub fn set(&mut self, key: String, value: String) {
90        self.values.insert(key, value);
91    }
92}