Files
sukr/src/config.rs
Timothy DeHerrera 4c2c3d5495 feat(cli): add configurable paths and --config flag
Enable monorepo support with CLI configuration:

- Add PathsConfig struct with serde defaults for content, output,
  static, and templates directories
- Add optional [paths] table to site.toml (backward compatible)
- Add -c/--config <FILE> flag to specify config file path
- Add -h/--help flag with usage information
- Resolve all paths relative to config file location

Users can now run multiple sites from a single repo:

  nrd-sh                             # uses ./site.toml
  nrd-sh -c sites/blog/site.toml     # looks in sites/blog/

Includes 2 new unit tests for path configuration parsing.
2026-01-31 15:26:22 -07:00

117 lines
3.3 KiB
Rust

//! Site configuration loading.
use crate::error::{Error, Result};
use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};
/// Site-wide configuration loaded from site.toml.
#[derive(Debug, Deserialize)]
pub struct SiteConfig {
/// Site title (used in page titles and nav).
pub title: String,
/// Site author name.
pub author: String,
/// Base URL for the site (used for feeds, canonical links).
pub base_url: String,
/// Path configuration (all optional with defaults).
#[serde(default)]
pub paths: PathsConfig,
}
/// Path configuration with sensible defaults.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct PathsConfig {
/// Content directory (default: "content")
pub content: PathBuf,
/// Output directory (default: "public")
pub output: PathBuf,
/// Static assets directory (default: "static")
#[serde(rename = "static")]
pub static_dir: PathBuf,
/// Templates directory (default: "templates")
pub templates: PathBuf,
}
impl Default for PathsConfig {
fn default() -> Self {
Self {
content: PathBuf::from("content"),
output: PathBuf::from("public"),
static_dir: PathBuf::from("static"),
templates: PathBuf::from("templates"),
}
}
}
impl SiteConfig {
/// Load configuration from a TOML file.
pub fn load(path: &Path) -> Result<Self> {
let content = fs::read_to_string(path).map_err(|e| Error::ReadFile {
path: path.to_path_buf(),
source: e,
})?;
toml::from_str(&content).map_err(|e| Error::Config {
path: path.to_path_buf(),
message: e.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_config() {
let toml = r#"
title = "Test Site"
author = "Test Author"
base_url = "https://example.com/"
"#;
let config: SiteConfig = toml::from_str(toml).unwrap();
assert_eq!(config.title, "Test Site");
assert_eq!(config.author, "Test Author");
assert_eq!(config.base_url, "https://example.com/");
}
#[test]
fn test_paths_config_defaults() {
let toml = r#"
title = "Test"
author = "Author"
base_url = "https://example.com"
"#;
let config: SiteConfig = toml::from_str(toml).unwrap();
assert_eq!(config.paths.content, PathBuf::from("content"));
assert_eq!(config.paths.output, PathBuf::from("public"));
assert_eq!(config.paths.static_dir, PathBuf::from("static"));
assert_eq!(config.paths.templates, PathBuf::from("templates"));
}
#[test]
fn test_paths_config_custom() {
let toml = r#"
title = "Test"
author = "Author"
base_url = "https://example.com"
[paths]
content = "src/content"
output = "dist"
static = "assets"
templates = "theme"
"#;
let config: SiteConfig = toml::from_str(toml).unwrap();
assert_eq!(config.paths.content, PathBuf::from("src/content"));
assert_eq!(config.paths.output, PathBuf::from("dist"));
assert_eq!(config.paths.static_dir, PathBuf::from("assets"));
assert_eq!(config.paths.templates, PathBuf::from("theme"));
}
}