use config::ConfigError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::{create_dir_all, write};
use std::path::Path;
use super::STAKPAK_API_ENDPOINT;
use super::profile::ProfileConfig;
use super::types::{OldAppConfig, Settings};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ConfigFile {
pub profiles: HashMap<String, ProfileConfig>,
pub settings: Settings,
}
impl Default for ConfigFile {
fn default() -> Self {
ConfigFile {
profiles: HashMap::new(),
settings: Settings {
machine_name: None,
auto_append_gitignore: Some(true),
anonymous_id: Some(uuid::Uuid::new_v4().to_string()),
collect_telemetry: Some(true),
editor: Some("nano".to_string()),
},
}
}
}
impl ConfigFile {
pub(crate) fn with_default_profile() -> Self {
ConfigFile {
profiles: HashMap::from([(
"default".into(),
ProfileConfig::with_api_endpoint(STAKPAK_API_ENDPOINT),
)]),
settings: Settings {
machine_name: None,
auto_append_gitignore: Some(true),
anonymous_id: Some(uuid::Uuid::new_v4().to_string()),
collect_telemetry: Some(true),
editor: Some("nano".to_string()),
},
}
}
pub(crate) fn profile_config(&self, profile_name: &str) -> Option<&ProfileConfig> {
self.profiles.get(profile_name)
}
pub(crate) fn profile_config_ok_or(
&self,
profile_name: &str,
) -> Result<ProfileConfig, ConfigError> {
self.profile_config(profile_name).cloned().ok_or_else(|| {
ConfigError::Message(format!(
"Profile '{}' not found in configuration",
profile_name
))
})
}
pub(crate) fn resolved_profile_config(
&self,
profile_name: &str,
) -> Result<ProfileConfig, ConfigError> {
let profile = self.profile_config_ok_or(profile_name)?;
Ok(profile.merge(self.profile_config("all")))
}
pub(crate) fn insert_app_config(&mut self, config: super::AppConfig) {
self.profiles
.insert(config.profile_name.clone(), config.into());
}
pub(crate) fn set_app_config_settings(&mut self, config: super::AppConfig) {
let existing_anonymous_id = self.settings.anonymous_id.clone();
let existing_collect_telemetry = self.settings.collect_telemetry;
let existing_editor = self.settings.editor.clone();
self.settings = Settings {
machine_name: config.machine_name,
auto_append_gitignore: config.auto_append_gitignore,
anonymous_id: config.anonymous_id.or(existing_anonymous_id),
collect_telemetry: config.collect_telemetry.or(existing_collect_telemetry),
editor: config.editor.or(existing_editor),
};
}
pub(crate) fn contains_readonly(&self) -> bool {
self.profiles.contains_key("readonly")
}
pub(crate) fn ensure_readonly(&mut self) -> bool {
if self.contains_readonly() {
false
} else {
self.profiles.insert(
"readonly".into(),
ProfileConfig::readonly_profile(self.profile_config("default")),
);
true
}
}
pub(crate) fn update_readonly(&mut self) {
self.profiles.insert(
"readonly".into(),
ProfileConfig::readonly_profile(self.profile_config("default")),
);
}
pub(crate) fn save_to<P: AsRef<Path>>(&self, path: P) -> Result<(), ConfigError> {
if let Some(parent) = path.as_ref().parent() {
create_dir_all(parent).map_err(|e| {
ConfigError::Message(format!("Failed to create config directory: {}", e))
})?;
}
let body = toml::to_string_pretty(self)
.map_err(|e| ConfigError::Message(format!("Failed to serialize config file: {}", e)))?;
write(path, body)
.map_err(|e| ConfigError::Message(format!("Failed to write config file: {}", e)))
}
}
impl From<OldAppConfig> for ConfigFile {
fn from(old_config: OldAppConfig) -> Self {
let settings: Settings = old_config.clone().into();
ConfigFile {
profiles: HashMap::from([(
"default".to_string(),
ProfileConfig::migrated_from_old_config(old_config),
)]),
settings,
}
}
}