use crate::{error::LemmyResult, location_info};
use anyhow::{Context, anyhow};
use deser_hjson::from_str;
use std::{env, fs, sync::LazyLock};
use structs::{PictrsConfig, Settings};
use url::Url;
use urlencoding::encode;
pub mod structs;
static DEFAULT_CONFIG_FILE: &str = "config/config.hjson";
const CONNECTION_OPTIONS: [&str; 1] = ["geqo_threshold=12"];
#[expect(clippy::expect_used)]
pub static SETTINGS: LazyLock<Settings> = LazyLock::new(|| {
if env::var("LEMMY_INITIALIZE_WITH_DEFAULT_SETTINGS").is_ok() {
println!(
"LEMMY_INITIALIZE_WITH_DEFAULT_SETTINGS was set, any configuration file has been ignored."
);
println!(
"Use with other environment variables to configure this instance further; e.g. LEMMY_DATABASE_URL."
);
Settings::default()
} else {
Settings::init().expect("Failed to load settings file, see documentation (https://join-lemmy.org/docs/en/administration/configuration.html).")
}
});
impl Settings {
pub(crate) fn init() -> LemmyResult<Self> {
let path =
env::var("LEMMY_CONFIG_LOCATION").unwrap_or_else(|_| DEFAULT_CONFIG_FILE.to_string());
let plain = fs::read_to_string(path)?;
let config = from_str::<Settings>(&plain)?;
if config.hostname == "unset" {
Err(anyhow!("Hostname variable is not set!").into())
} else {
Ok(config)
}
}
pub fn get_database_url(&self) -> String {
if let Ok(url) = env::var("LEMMY_DATABASE_URL") {
url
} else {
self.database.connection.clone()
}
}
fn get_protocol_string(&self) -> &'static str {
if self.tls_enabled { "https" } else { "http" }
}
pub fn get_protocol_and_hostname(&self) -> String {
format!("{}://{}", self.get_protocol_string(), self.hostname)
}
pub fn get_hostname_without_port(&self) -> Result<String, anyhow::Error> {
Ok(
(*self
.hostname
.split(':')
.collect::<Vec<&str>>()
.first()
.context(location_info!())?)
.to_string(),
)
}
pub fn pictrs(&self) -> LemmyResult<PictrsConfig> {
self
.pictrs
.clone()
.ok_or_else(|| anyhow!("images_disabled").into())
}
pub fn get_database_url_with_options(&self) -> LemmyResult<String> {
let mut url = Url::parse(&self.get_database_url())?;
let lemmy_protocol_and_hostname_option =
"lemmy.protocol_and_hostname=".to_owned() + &self.get_protocol_and_hostname();
let mut options = CONNECTION_OPTIONS.to_vec();
options.push(&lemmy_protocol_and_hostname_option);
let options_segments = options
.iter()
.map(|o| format!("-c {}", encode(o)))
.collect::<Vec<String>>()
.join(" ");
url.set_query(Some(&format!("options={options_segments}")));
Ok(url.into())
}
}
#[expect(clippy::expect_used)]
fn pictrs_placeholder_url() -> Url {
Url::parse("http://localhost:8080").expect("parse pictrs url")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_config() -> LemmyResult<()> {
Settings::init()?;
Ok(())
}
}