use crate::error::Result;
use crate::paths::Paths;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub index: IndexConfig,
#[serde(default)]
pub install: InstallConfig,
#[serde(default)]
pub cache: CacheConfig,
#[serde(default)]
pub analytics: AnalyticsConfig,
#[serde(default)]
pub security: SecurityConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexConfig {
#[serde(default = "default_base_url")]
pub base_url: String,
#[serde(default = "default_true")]
pub auto_update: bool,
#[serde(default = "default_update_interval")]
pub update_interval: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstallConfig {
#[serde(default = "default_cellar")]
pub cellar: String,
#[serde(default = "default_prefix")]
pub prefix: String,
#[serde(default = "default_parallel")]
pub parallel_downloads: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
#[serde(default = "default_max_size")]
pub max_size: String,
#[serde(default = "default_formula_ttl")]
pub formula_ttl: u64,
#[serde(default = "default_download_ttl")]
pub download_ttl: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsConfig {
#[serde(default)]
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
#[serde(default = "default_require_signature")]
pub require_signature: bool,
#[serde(default = "default_allow_unsigned")]
pub allow_unsigned: bool,
#[serde(default = "default_max_signature_age")]
pub max_signature_age: u64,
#[serde(default)]
pub additional_trusted_keys: Vec<String>,
}
fn default_base_url() -> String {
"https://raw.githubusercontent.com/neul-labs/stout-index/main".to_string()
}
fn default_true() -> bool {
true
}
fn default_update_interval() -> u64 {
1800 }
fn default_cellar() -> String {
"/opt/homebrew/Cellar".to_string()
}
fn default_prefix() -> String {
"/opt/homebrew".to_string()
}
fn default_parallel() -> u32 {
4
}
fn default_max_size() -> String {
"2GB".to_string()
}
fn default_formula_ttl() -> u64 {
86400 }
fn default_download_ttl() -> u64 {
604800 }
fn default_require_signature() -> bool {
cfg!(not(debug_assertions))
}
fn default_allow_unsigned() -> bool {
cfg!(debug_assertions)
}
fn default_max_signature_age() -> u64 {
604800 }
impl Default for IndexConfig {
fn default() -> Self {
Self {
base_url: default_base_url(),
auto_update: default_true(),
update_interval: default_update_interval(),
}
}
}
impl Default for InstallConfig {
fn default() -> Self {
Self {
cellar: default_cellar(),
prefix: default_prefix(),
parallel_downloads: default_parallel(),
}
}
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
max_size: default_max_size(),
formula_ttl: default_formula_ttl(),
download_ttl: default_download_ttl(),
}
}
}
impl Default for AnalyticsConfig {
fn default() -> Self {
Self {
enabled: false, }
}
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
require_signature: default_require_signature(),
allow_unsigned: default_allow_unsigned(),
max_signature_age: default_max_signature_age(),
additional_trusted_keys: vec![],
}
}
}
impl SecurityConfig {
pub fn to_security_policy(&self) -> stout_index::SecurityPolicy {
stout_index::SecurityPolicy {
require_signature: self.require_signature,
max_signature_age: self.max_signature_age,
additional_keys: self.additional_trusted_keys.clone(),
allow_unsigned: self.allow_unsigned,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
index: IndexConfig::default(),
install: InstallConfig::default(),
cache: CacheConfig::default(),
analytics: AnalyticsConfig::default(),
security: SecurityConfig::default(),
}
}
}
impl Config {
pub fn load(paths: &Paths) -> Result<Self> {
let config_path = paths.config_file();
if config_path.exists() {
let contents = std::fs::read_to_string(&config_path)?;
let config: Config = toml::from_str(&contents)?;
Ok(config)
} else {
Ok(Self::default())
}
}
pub fn save(&self, paths: &Paths) -> Result<()> {
let config_path = paths.config_file();
if let Some(parent) = config_path.parent() {
std::fs::create_dir_all(parent)?;
}
let contents = toml::to_string_pretty(self)?;
std::fs::write(&config_path, contents)?;
Ok(())
}
}