use crate::types::{OpenSkyError, Result};
use configparser::ini::Ini;
use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub struct Config {
pub username: Option<String>,
pub password: Option<String>,
pub client_id: Option<String>,
pub client_secret: Option<String>,
pub cache_purge: Option<String>,
}
impl Config {
pub fn load() -> Result<Self> {
let config_path = Self::config_path()?;
Self::load_from_path(&config_path)
}
pub fn load_from_path(path: &PathBuf) -> Result<Self> {
if !path.exists() {
return Err(OpenSkyError::Config(format!(
"Config file not found: {}. Run `ostk pyopensky config set` to create it.",
path.display()
)));
}
let mut ini = Ini::new();
ini.load(path).map_err(|e| OpenSkyError::Config(e))?;
let config = Config {
username: ini.get("default", "username").filter(|s| !s.is_empty()),
password: ini.get("default", "password").filter(|s| !s.is_empty()),
client_id: ini.get("default", "client_id").filter(|s| !s.is_empty()),
client_secret: ini.get("default", "client_secret").filter(|s| !s.is_empty()),
cache_purge: ini.get("cache", "purge").filter(|s| !s.is_empty()),
};
Ok(config)
}
pub fn config_dir() -> Result<PathBuf> {
#[cfg(target_os = "linux")]
{
dirs::config_dir()
.map(|p| p.join("opensky"))
.ok_or_else(|| OpenSkyError::Config("Could not determine config directory".into()))
}
#[cfg(target_os = "macos")]
{
dirs::config_dir()
.map(|p| p.join("opensky"))
.ok_or_else(|| OpenSkyError::Config("Could not determine config directory".into()))
}
#[cfg(target_os = "windows")]
{
dirs::data_local_dir()
.map(|p| p.join("opensky"))
.ok_or_else(|| OpenSkyError::Config("Could not determine config directory".into()))
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
dirs::home_dir()
.map(|p| p.join(".opensky"))
.ok_or_else(|| OpenSkyError::Config("Could not determine home directory".into()))
}
}
pub fn config_path() -> Result<PathBuf> {
Ok(Self::config_dir()?.join("settings.conf"))
}
pub fn has_credentials(&self) -> bool {
self.username.is_some() && self.password.is_some()
}
pub fn require_username(&self) -> Result<&str> {
self.username
.as_deref()
.ok_or_else(|| OpenSkyError::Config("Username not configured".into()))
}
pub fn require_password(&self) -> Result<&str> {
self.password
.as_deref()
.ok_or_else(|| OpenSkyError::Config("Password not configured".into()))
}
pub fn save(&self) -> Result<()> {
let config_path = Self::config_path()?;
self.save_to_path(&config_path)
}
pub fn save_to_path(&self, path: &PathBuf) -> Result<()> {
use std::fs;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let mut ini = Ini::new();
if let Some(ref u) = self.username {
ini.set("default", "username", Some(u.clone()));
}
if let Some(ref p) = self.password {
ini.set("default", "password", Some(p.clone()));
}
if let Some(ref c) = self.client_id {
ini.set("default", "client_id", Some(c.clone()));
}
if let Some(ref c) = self.client_secret {
ini.set("default", "client_secret", Some(c.clone()));
}
if let Some(ref p) = self.cache_purge {
ini.set("cache", "purge", Some(p.clone()));
}
ini.write(path).map_err(|e| OpenSkyError::Config(e.to_string()))?;
Ok(())
}
}
pub const DEFAULT_CONFIG: &str = r#"[default]
username =
password =
client_id =
client_secret =
[cache]
purge = 90 days
"#;
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_load_config() {
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(
temp_file,
r#"[default]
username = testuser
password = testpass
[cache]
purge = 30 days
"#
)
.unwrap();
let config = Config::load_from_path(&temp_file.path().to_path_buf()).unwrap();
assert_eq!(config.username, Some("testuser".to_string()));
assert_eq!(config.password, Some("testpass".to_string()));
assert_eq!(config.cache_purge, Some("30 days".to_string()));
assert!(config.has_credentials());
}
#[test]
fn test_empty_values_treated_as_none() {
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(
temp_file,
r#"[default]
username =
password =
"#
)
.unwrap();
let config = Config::load_from_path(&temp_file.path().to_path_buf()).unwrap();
assert_eq!(config.username, None);
assert_eq!(config.password, None);
assert!(!config.has_credentials());
}
}