studium/src/utils/config.rs
Antoine Pelletier 5e160ae11c wip
2026-09-29 09:47:27 +02:00

97 lines
3.3 KiB
Rust

use std::{path::Path, sync::OnceLock};
use config::Config;
use serde::Deserialize;
#[derive(Deserialize, Clone)]
pub struct ServerConfig {
pub address: Option<String>,
pub port: Option<u16>,
}
#[derive(Deserialize, Clone)]
pub struct PostgresConfig {
pub host: Option<String>,
pub port: Option<u16>,
pub user: String,
pub password: String,
pub name: String,
}
/// Rendering of the lecture notes to pdf
#[derive(Deserialize, Clone)]
pub struct NotesConfig {
/// Directory of `template.tex`, `box-filter.lua` and `assets/`
pub template_dir: String,
/// Logo shown at the top right, relative to `template_dir`
pub logo: Option<String>,
/// Shown in the header of the pdf
pub student_name: Option<String>,
pub student_id: Option<String>,
}
/// TimeTagger account the focus sessions are counted in
#[derive(Deserialize, Clone)]
pub struct TimetaggerConfig {
/// Api of the TimeTagger server, `https://timetagger.app/api/v2` by default
pub url: Option<String>,
/// Api token, from the account page of TimeTagger
pub token: String,
}
#[derive(Deserialize, Clone)]
pub struct AppConfig {
pub server: ServerConfig,
pub postgres: PostgresConfig,
/// Directory containing the built frontend
pub frontend_dir: String,
/// Where the files of the courses and associations are stored (lecture notes,
/// pdf...), one folder each. A local directory standing in for the NAS for now.
pub storage_dir: String,
pub notes: NotesConfig,
/// Optional: without it the pomodoro only counts the time in the app
pub timetagger: Option<TimetaggerConfig>,
}
impl AppConfig {
pub fn get_bind_address(&self) -> String {
let address = self.server.address.clone().unwrap_or("0.0.0.0".to_owned());
let port = self.server.port.unwrap_or(3000);
format!("{}:{}", address, port)
}
pub fn get_postgresql_url(&self) -> String {
let host = self.postgres.host.clone().unwrap_or("localhost".to_owned());
let port = self.postgres.port.unwrap_or(5432);
let user = self.postgres.user.clone();
let password = self.postgres.password.clone();
let name = self.postgres.name.clone();
format!("postgres://{user}:{password}@{host}:{port}/{name}")
}
}
/// Loads the configuration, by decreasing priority:
/// - `APP__SERVER__PORT=3000` style environment variables
/// - `./config.yml`
/// - the file pointed by `$APP_CONFIG` (`/etc/studium/config.yml` by default)
pub fn get() -> &'static AppConfig {
static CONFIG: OnceLock<AppConfig> = OnceLock::new();
CONFIG.get_or_init(|| {
let config_path =
std::env::var("APP_CONFIG").unwrap_or("/etc/studium/config.yml".to_owned());
let config_path = Path::new(&config_path);
let raw = Config::builder()
.add_source(config::File::from(config_path).required(false))
.add_source(config::File::with_name("config").required(false))
.add_source(
config::Environment::with_prefix("APP")
.separator("__")
.ignore_empty(true),
)
.build()
.expect("Invalid config source");
raw.try_deserialize::<AppConfig>()
.expect("Invalid config format")
})
}