215 lines
7.2 KiB
Rust
215 lines
7.2 KiB
Rust
use crate::env;
|
|
use crate::tilde;
|
|
use crate::Ini;
|
|
use crate::Path;
|
|
use crate::PathBuf;
|
|
|
|
lazy_static::lazy_static! {
|
|
pub static ref SETTINGS: Settings = Settings::load().expect("Failed to load settings");
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Settings {
|
|
pub block_path: String,
|
|
pub torrent_path: String,
|
|
pub db_path: String,
|
|
pub wallet_path: String,
|
|
pub wallet_name: String,
|
|
pub balance_sheet: String,
|
|
pub log_path: String,
|
|
pub log_level: String,
|
|
pub public_ip: String,
|
|
pub listen_ip: String,
|
|
pub rpc_port: String,
|
|
pub testnet_rpc_port: String,
|
|
pub validator: bool,
|
|
pub outgoing_connections: u8,
|
|
pub incoming_connections: u8,
|
|
pub threads: u16,
|
|
pub piggybacks: Vec<String>,
|
|
pub pg_host: String,
|
|
pub pg_port: u16,
|
|
pub pg_user: String,
|
|
pub pg_password: Option<String>,
|
|
pub pg_dbname: String,
|
|
}
|
|
|
|
impl Settings {
|
|
fn expand(path: &str, base_dir: &Path) -> String {
|
|
let expanded = PathBuf::from(tilde(path).as_ref());
|
|
if expanded.is_relative() {
|
|
base_dir.join(expanded).to_string_lossy().into_owned()
|
|
} else {
|
|
expanded.to_string_lossy().into_owned()
|
|
}
|
|
}
|
|
|
|
fn get_config_path() -> PathBuf {
|
|
// Priority: CLI arg > ENV var > ./settings.ini > executable directory > platform fallback
|
|
let mut args = env::args().skip(1); // Skip the executable name
|
|
|
|
while let Some(arg) = args.next() {
|
|
if arg == "--config" {
|
|
if let Some(path) = args.next() {
|
|
return PathBuf::from(path);
|
|
} else {
|
|
eprintln!("Error: --config flag requires a path");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
if let Ok(path) = env::var("SETTINGS_PATH") {
|
|
return PathBuf::from(path);
|
|
}
|
|
|
|
let local_settings = env::current_dir()
|
|
.unwrap_or_else(|_| PathBuf::from("."))
|
|
.join("settings.ini");
|
|
|
|
if local_settings.exists() {
|
|
return local_settings;
|
|
}
|
|
|
|
if let Ok(exe_path) = env::current_exe() {
|
|
if let Some(exe_dir) = exe_path.parent() {
|
|
let exe_settings = exe_dir.join("settings.ini");
|
|
if exe_settings.exists() {
|
|
return exe_settings;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
{
|
|
PathBuf::from("/etc/contractless/settings.ini")
|
|
}
|
|
#[cfg(not(unix))]
|
|
{
|
|
env::current_dir()
|
|
.unwrap_or_else(|_| PathBuf::from("."))
|
|
.join("settings.ini")
|
|
}
|
|
}
|
|
|
|
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
|
let config_file_path = Self::get_config_path();
|
|
let config_dir = config_file_path.parent().unwrap_or_else(|| Path::new("."));
|
|
|
|
let conf = Ini::load_from_file(&config_file_path)?;
|
|
|
|
let section = conf
|
|
.section(Some("Piggyback"))
|
|
.ok_or("Piggyback section not found")?;
|
|
#[cfg(feature = "mainnet")]
|
|
let pg_section = conf
|
|
.section(Some("Postgres"))
|
|
.ok_or("Postgres section not found")?;
|
|
#[cfg(feature = "testnet")]
|
|
let pg_section = conf
|
|
.section(Some("Postgres-Testnet"))
|
|
.ok_or("Postgres-Testnet section not found")?;
|
|
|
|
let mut piggybacks = Vec::new();
|
|
for (key, value) in section.iter() {
|
|
if key.to_uppercase().starts_with("PIGGYBACK_") {
|
|
piggybacks.push(value.to_string());
|
|
}
|
|
}
|
|
|
|
let threads = conf
|
|
.get_from(Some("Settings"), "THREADS")
|
|
.unwrap_or("1")
|
|
.parse::<u16>()?;
|
|
|
|
if threads != 1 && threads != 2 && threads % 4 != 0 {
|
|
return Err("THREADS must be 1, 2, or a multiple of 4".into());
|
|
}
|
|
|
|
if threads == 0 || threads > 256 {
|
|
return Err("THREADS must be between 1 and 256".into());
|
|
}
|
|
|
|
Ok(Settings {
|
|
block_path: Self::expand(
|
|
conf.get_from(Some("Paths"), "BLOCK_PATH")
|
|
.ok_or("BLOCK_PATH not found")?,
|
|
config_dir,
|
|
),
|
|
torrent_path: Self::expand(
|
|
conf.get_from(Some("Paths"), "TORRENT_PATH")
|
|
.ok_or("TORRENT_PATH not found")?,
|
|
config_dir,
|
|
),
|
|
db_path: Self::expand(
|
|
conf.get_from(Some("Paths"), "DB_PATH")
|
|
.ok_or("DB_PATH not found")?,
|
|
config_dir,
|
|
),
|
|
wallet_path: Self::expand(
|
|
conf.get_from(Some("Paths"), "WALLET_PATH")
|
|
.ok_or("WALLET_PATH not found")?,
|
|
config_dir,
|
|
),
|
|
wallet_name: conf
|
|
.get_from(Some("Paths"), "WALLET_NAME")
|
|
.ok_or("WALLET_NAME not found")?
|
|
.to_string(),
|
|
balance_sheet: Self::expand(
|
|
conf.get_from(Some("Paths"), "BALANCE_SHEET")
|
|
.ok_or("BALANCE_SHEET not found")?,
|
|
config_dir,
|
|
),
|
|
log_path: Self::expand(
|
|
conf.get_from(Some("Paths"), "LOG_PATH").unwrap_or("./logs"),
|
|
config_dir,
|
|
),
|
|
log_level: conf
|
|
.get_from(Some("Settings"), "LOG_LEVEL")
|
|
.unwrap_or("info")
|
|
.to_string(),
|
|
public_ip: conf
|
|
.get_from(Some("Settings"), "PUBLIC_IP")
|
|
.or_else(|| conf.get_from(Some("Settings"), "IP"))
|
|
.ok_or("PUBLIC_IP not found")?
|
|
.to_string(),
|
|
listen_ip: conf
|
|
.get_from(Some("Settings"), "LISTEN_IP")
|
|
.unwrap_or("0.0.0.0")
|
|
.to_string(),
|
|
rpc_port: conf
|
|
.get_from(Some("Settings"), "RPC_PORT")
|
|
.ok_or("RPC_PORT not found")?
|
|
.to_string(),
|
|
testnet_rpc_port: conf
|
|
.get_from(Some("Settings"), "TESTNET_RPC_PORT")
|
|
.ok_or("TESTNET_RPC_PORT not found")?
|
|
.to_string(),
|
|
validator: conf
|
|
.get_from(Some("Settings"), "VALIDATOR")
|
|
.unwrap_or("false")
|
|
.parse::<bool>()?,
|
|
outgoing_connections: conf
|
|
.get_from(Some("Settings"), "OUTGOING_CONNECTIONS")
|
|
.ok_or("OUTGOING_CONNECTIONS not found")?
|
|
.parse::<u8>()?,
|
|
incoming_connections: conf
|
|
.get_from(Some("Settings"), "INCOMING_CONNECTIONS")
|
|
.ok_or("INCOMING_CONNECTIONS not found")?
|
|
.parse::<u8>()?,
|
|
threads,
|
|
pg_host: pg_section.get("host").unwrap_or("127.0.0.1").to_string(),
|
|
pg_port: pg_section.get("port").unwrap_or("5432").parse::<u16>()?,
|
|
pg_user: pg_section
|
|
.get("user")
|
|
.ok_or("postgres user not set")?
|
|
.to_string(),
|
|
pg_password: pg_section.get("password").map(|s| s.to_string()),
|
|
pg_dbname: pg_section
|
|
.get("dbname")
|
|
.ok_or("postgres dbname not set")?
|
|
.to_string(),
|
|
piggybacks,
|
|
})
|
|
}
|
|
}
|