switch config to env vars, add nix module

This commit is contained in:
Lewis Wynne 2026-04-09 14:24:46 +01:00
parent 996da6cf8b
commit 5bfba1b6ff
8 changed files with 206 additions and 32 deletions

View file

@ -1,9 +1,9 @@
use serde::Deserialize;
use std::env;
use std::path::PathBuf;
#[derive(Debug, Deserialize)]
#[derive(Debug)]
pub struct Config {
pub listen: String,
pub port: u16,
pub data_dir: PathBuf,
pub site_title: String,
pub site_url: String,
@ -12,10 +12,28 @@ pub struct Config {
}
impl Config {
pub fn load(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let config: Config = toml::from_str(&content)?;
Ok(config)
pub fn listen_addr(&self) -> String {
format!("127.0.0.1:{}", self.port)
}
pub fn from_env() -> Result<Self, String> {
Ok(Config {
port: env::var("BOOK_PORT")
.unwrap_or_else(|_| "8123".into())
.parse()
.map_err(|_| "BOOK_PORT must be a number")?,
data_dir: env::var("BOOK_DATA_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("./data")),
site_title: env::var("BOOK_SITE_TITLE").unwrap_or_else(|_| "guestbook".into()),
site_url: env::var("BOOK_SITE_URL").map_err(|_| "BOOK_SITE_URL is required")?,
telegram_bot_token: env::var("BOOK_TELEGRAM_BOT_TOKEN")
.map_err(|_| "BOOK_TELEGRAM_BOT_TOKEN is required")?,
telegram_chat_id: env::var("BOOK_TELEGRAM_CHAT_ID")
.map_err(|_| "BOOK_TELEGRAM_CHAT_ID is required")?
.parse()
.map_err(|_| "BOOK_TELEGRAM_CHAT_ID must be an integer")?,
})
}
}
@ -24,20 +42,54 @@ mod tests {
use super::*;
#[test]
fn test_parse_config() {
let toml_str = r#"
listen = "127.0.0.1:8123"
data_dir = "/var/lib/guestbook"
site_title = "ily.rs"
site_url = "https://ily.rs"
telegram_bot_token = "123:ABC"
telegram_chat_id = 12345
"#;
let config: Config = toml::from_str(toml_str).unwrap();
assert_eq!(config.listen, "127.0.0.1:8123");
assert_eq!(config.data_dir, PathBuf::from("/var/lib/guestbook"));
assert_eq!(config.site_title, "ily.rs");
assert_eq!(config.site_url, "https://ily.rs");
fn test_from_env() {
env::set_var("BOOK_PORT", "9999");
env::set_var("BOOK_DATA_DIR", "/tmp/gb");
env::set_var("BOOK_SITE_TITLE", "test.rs");
env::set_var("BOOK_SITE_URL", "https://test.rs");
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
let config = Config::from_env().unwrap();
assert_eq!(config.port, 9999);
assert_eq!(config.listen_addr(), "127.0.0.1:9999");
assert_eq!(config.data_dir, PathBuf::from("/tmp/gb"));
assert_eq!(config.site_title, "test.rs");
assert_eq!(config.site_url, "https://test.rs");
assert_eq!(config.telegram_chat_id, 12345);
// Clean up
env::remove_var("BOOK_PORT");
env::remove_var("BOOK_DATA_DIR");
env::remove_var("BOOK_SITE_TITLE");
env::remove_var("BOOK_SITE_URL");
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
}
#[test]
fn test_defaults() {
env::set_var("BOOK_SITE_URL", "https://test.rs");
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
let config = Config::from_env().unwrap();
assert_eq!(config.port, 8123);
assert_eq!(config.data_dir, PathBuf::from("./data"));
assert_eq!(config.site_title, "guestbook");
env::remove_var("BOOK_SITE_URL");
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
}
#[test]
fn test_missing_required() {
env::remove_var("BOOK_SITE_URL");
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
let result = Config::from_env();
assert!(result.is_err());
}
}

View file

@ -11,8 +11,9 @@ use teloxide::prelude::*;
async fn main() {
tracing_subscriber::fmt::init();
let config = config::Config::load("config.toml").expect("failed to load config.toml");
let listen = config.listen.clone();
dotenvy::dotenv().ok();
let config = config::Config::from_env().expect("failed to load config");
let listen = config.listen_addr();
let entries_dir = config.data_dir.join("entries");
let chat_id = ChatId(config.telegram_chat_id);

View file

@ -8,7 +8,7 @@ pub fn render_page(site_title: &str, site_url: &str, entries: &[Entry], form_htm
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>guestbook - {site_title}</title>
<title>{site_title}</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>