use std::net::SocketAddr;
use std::path::PathBuf;
use crate::error::{Error, Result};
pub fn data_dir() -> Result<PathBuf> {
let base = if cfg!(target_os = "macos") {
home_dir()?.join("Library").join("Application Support")
} else if cfg!(target_os = "windows") {
std::env::var("APPDATA")
.map(PathBuf::from)
.unwrap_or_else(|_| home_dir().unwrap().join("AppData").join("Roaming"))
} else {
std::env::var("XDG_DATA_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| home_dir().unwrap().join(".local").join("share"))
};
Ok(base.join("ant"))
}
pub fn config_dir() -> Result<PathBuf> {
let base = if cfg!(target_os = "macos") {
home_dir()?.join("Library").join("Application Support")
} else if cfg!(target_os = "windows") {
std::env::var("APPDATA")
.map(PathBuf::from)
.unwrap_or_else(|_| home_dir().unwrap().join("AppData").join("Roaming"))
} else {
std::env::var("XDG_CONFIG_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| home_dir().unwrap().join(".config"))
};
Ok(base.join("ant"))
}
pub fn log_dir() -> Result<PathBuf> {
if cfg!(target_os = "macos") {
Ok(home_dir()?.join("Library").join("Logs").join("ant"))
} else {
Ok(data_dir()?.join("logs"))
}
}
pub fn load_bootstrap_peers() -> Result<Option<Vec<SocketAddr>>> {
let path = config_dir()?.join("bootstrap_peers.toml");
if !path.exists() {
return Ok(None);
}
let contents = std::fs::read_to_string(&path)?;
let config: BootstrapConfig =
toml::from_str(&contents).map_err(|e| Error::BootstrapConfigParse(e.to_string()))?;
let addrs: Vec<SocketAddr> = config.peers.iter().filter_map(|s| s.parse().ok()).collect();
if addrs.is_empty() {
return Ok(None);
}
Ok(Some(addrs))
}
#[derive(serde::Deserialize)]
struct BootstrapConfig {
peers: Vec<String>,
}
fn home_dir() -> Result<PathBuf> {
std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.map(PathBuf::from)
.map_err(|_| Error::HomeDirNotFound)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_dir_ends_with_ant() {
let dir = data_dir().unwrap();
assert_eq!(dir.file_name().unwrap(), "ant");
}
#[test]
fn config_dir_ends_with_ant() {
let dir = config_dir().unwrap();
assert_eq!(dir.file_name().unwrap(), "ant");
}
#[test]
fn log_dir_contains_ant() {
let dir = log_dir().unwrap();
assert!(
dir.components().any(|c| c.as_os_str() == "ant"),
"log_dir should contain 'ant' component: {:?}",
dir
);
}
#[test]
fn load_bootstrap_peers_returns_none_when_no_file() {
let _result = load_bootstrap_peers();
}
#[test]
fn parse_bootstrap_config() {
let toml_str = r#"
peers = [
"129.212.138.135:10000",
"134.199.138.183:10000",
]
"#;
let config: BootstrapConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.peers.len(), 2);
let addr: SocketAddr = config.peers[0].parse().unwrap();
assert_eq!(addr.port(), 10000);
}
}