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.
This commit is contained in:
Timothy DeHerrera
2026-01-31 15:26:22 -07:00
parent 0d2c460f52
commit 4c2c3d5495
2 changed files with 138 additions and 24 deletions

View File

@@ -3,7 +3,7 @@
use crate::error::{Error, Result};
use serde::Deserialize;
use std::fs;
use std::path::Path;
use std::path::{Path, PathBuf};
/// Site-wide configuration loaded from site.toml.
#[derive(Debug, Deserialize)]
@@ -14,6 +14,35 @@ pub struct SiteConfig {
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 {
@@ -48,4 +77,40 @@ mod tests {
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"));
}
}