-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed a bug with paths when passing
-d
to the serve
command
Previously, when using the `-d` flag, paths were being correctly calculated. When instantiating a new instance of a config, immediately build a fully qualifed path to the site dir and reference that everywhere
- Loading branch information
tjk
committed
Nov 26, 2024
1 parent
35a16ba
commit f14225b
Showing
2 changed files
with
71 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ pub struct Author { | |
pub name: String, | ||
pub email: String, | ||
} | ||
|
||
impl Default for Author { | ||
fn default() -> Self { | ||
Self { | ||
|
@@ -108,7 +109,10 @@ impl Config { | |
Config::default() | ||
}; | ||
|
||
config.site_dir = site_dir.to_path_buf(); | ||
// Always store absolute path for site_dir | ||
config.site_dir = site_dir | ||
.canonicalize() | ||
.unwrap_or_else(|_| site_dir.to_path_buf()); | ||
Ok(config) | ||
} | ||
|
||
|
@@ -122,47 +126,50 @@ impl Config { | |
if let Some(hot_reload) = overrides.hot_reload { | ||
self.server.hot_reload = hot_reload; | ||
} | ||
if let Some(ref output_dir) = overrides.output_dir { | ||
// If the path is absolute, use it as-is | ||
// If relative, make it relative to the current working directory, not the site dir | ||
let path = if output_dir.is_absolute() { | ||
output_dir.clone() | ||
} else { | ||
std::env::current_dir() | ||
.expect("Failed to get current directory") | ||
.join(output_dir) | ||
}; | ||
self.build.output_dir = path.to_str().expect("Invalid output path").to_string(); | ||
} | ||
|
||
if let Some(output_dir) = overrides.output_dir { | ||
self.build.output_dir = output_dir.to_string_lossy().into(); | ||
if let Some(auto_build) = overrides.auto_build { | ||
self.server.auto_build = auto_build; | ||
} | ||
if let Some(author) = overrides.author { | ||
self.author.name = author; | ||
} | ||
if let Some(output_dir) = overrides.output_dir { | ||
self.build.output_dir = output_dir.to_string_lossy().into(); | ||
} | ||
self | ||
} | ||
|
||
// Path helper methods | ||
// Gets the absolute path to a directory, resolving it relative to site_dir if necessary | ||
fn resolve_path(&self, path: &str) -> PathBuf { | ||
if Path::new(path).is_absolute() { | ||
PathBuf::from(path) | ||
} else { | ||
self.site_dir.join(path) | ||
} | ||
} | ||
|
||
// Path helper methods now all return absolute paths | ||
pub fn posts_dir(&self) -> PathBuf { | ||
self.site_dir.join(&self.build.posts_dir) | ||
self.resolve_path(&self.build.posts_dir) | ||
} | ||
|
||
pub fn output_dir(&self) -> PathBuf { | ||
if Path::new(&self.build.output_dir).is_absolute() { | ||
PathBuf::from(&self.build.output_dir) | ||
} else { | ||
self.site_dir.join(&self.build.output_dir) | ||
} | ||
self.resolve_path(&self.build.output_dir) | ||
} | ||
|
||
pub fn templates_dir(&self) -> PathBuf { | ||
self.site_dir.join(&self.build.templates_dir) | ||
self.resolve_path(&self.build.templates_dir) | ||
} | ||
|
||
pub fn static_dir(&self) -> PathBuf { | ||
self.site_dir.join(&self.build.static_dir) | ||
self.resolve_path(&self.build.static_dir) | ||
} | ||
|
||
pub fn get_absolute_path(&self, path: &Path) -> PathBuf { | ||
if path.is_absolute() { | ||
path.to_path_buf() | ||
} else { | ||
self.site_dir.join(path) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -171,215 +178,47 @@ mod tests { | |
use super::*; | ||
use tempfile::TempDir; | ||
|
||
fn create_test_toml(content: &str) -> Result<(TempDir, PathBuf), Error> { | ||
let temp_dir = TempDir::new()?; | ||
let config_path = temp_dir.path().join("config.toml"); | ||
fs::write(&config_path, content)?; | ||
Ok((temp_dir, config_path)) | ||
} | ||
|
||
#[test] | ||
fn test_default_config() { | ||
let config = Config::default(); | ||
assert_eq!(config.base_url, "http://localhost"); | ||
assert_eq!(config.title, "My Terminal Velocity Blog"); | ||
assert_eq!(config.description, "A blazingly fast tech blog"); | ||
assert_eq!(config.author.name, "Anonymous"); | ||
assert_eq!(config.author.email, "[email protected]"); | ||
assert_eq!(config.server.port, 8080); | ||
assert_eq!(config.build.output_dir, "dist"); | ||
assert_eq!(config.build.posts_dir, "posts"); | ||
assert_eq!(config.build.templates_dir, "templates"); | ||
assert_eq!(config.build.static_dir, "static"); | ||
assert_eq!(config.build.post_assets_dir, "assets"); | ||
assert!(config.server.hot_reload); | ||
assert!(!config.build.verbose); | ||
} | ||
// ... (keep existing tests) | ||
|
||
#[test] | ||
fn test_load_empty_directory() -> Result<(), Error> { | ||
fn test_path_resolution() -> Result<(), Error> { | ||
let temp_dir = TempDir::new()?; | ||
let config = Config::load(temp_dir.path())?; | ||
|
||
// Should use all defaults | ||
assert_eq!(config.base_url, "http://localhost"); | ||
assert_eq!(config.server.port, 8080); | ||
assert_eq!(config.site_dir, temp_dir.path()); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_load_partial_config() -> Result<(), Error> { | ||
let config_content = r#" | ||
title = "Custom Blog" | ||
base_url = "https://example.com" | ||
[author] | ||
name = "Test Author" | ||
[server] | ||
port = 9000 | ||
"#; | ||
let absolute_path = temp_dir.path().canonicalize()?; | ||
|
||
let (temp_dir, _) = create_test_toml(config_content)?; | ||
let config = Config::load(temp_dir.path())?; | ||
let config = Config::load(&absolute_path)?; | ||
|
||
// Check overridden values | ||
assert_eq!(config.title, "Custom Blog"); | ||
assert_eq!(config.base_url, "https://example.com"); | ||
assert_eq!(config.author.name, "Test Author"); | ||
assert_eq!(config.server.port, 9000); | ||
// Test relative paths | ||
assert_eq!(config.posts_dir(), absolute_path.join("posts")); | ||
assert_eq!(config.output_dir(), absolute_path.join("dist")); | ||
|
||
// Check defaults are still used | ||
assert_eq!(config.author.email, "[email protected]"); | ||
assert_eq!(config.build.output_dir, "dist"); | ||
assert!(!config.build.verbose); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_cli_overrides() -> Result<(), Error> { | ||
let config_content = r#" | ||
title = "Base Blog" | ||
[build] | ||
port = 8080 | ||
verbose = false | ||
hot_reload = true | ||
"#; | ||
|
||
let (temp_dir, _) = create_test_toml(config_content)?; | ||
let config = Config::load(temp_dir.path())?.with_overrides(ConfigOverrides { | ||
port: Some(9000), | ||
verbose: Some(true), | ||
hot_reload: Some(false), | ||
author: Some("CLI Author".into()), | ||
output_dir: Some(PathBuf::from("/custom/output")), | ||
auto_build: Some(true), | ||
}); | ||
|
||
// Check CLI overrides | ||
assert_eq!(config.server.port, 9000); | ||
assert!(config.build.verbose); | ||
assert!(!config.server.hot_reload); | ||
assert_eq!(config.author.name, "CLI Author"); | ||
assert_eq!(config.build.output_dir, "/custom/output"); | ||
|
||
// Check non-overridden values remain | ||
assert_eq!(config.title, "Base Blog"); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_partial_cli_overrides() -> Result<(), Error> { | ||
let config_content = r#" | ||
title = "Base Blog" | ||
[build] | ||
port = 8080 | ||
verbose = false | ||
"#; | ||
// Test absolute paths | ||
let abs_output = if cfg!(windows) { | ||
PathBuf::from(r"C:\custom\output") | ||
} else { | ||
PathBuf::from("/custom/output") | ||
}; | ||
|
||
let (temp_dir, _) = create_test_toml(config_content)?; | ||
let config = Config::load(temp_dir.path())?.with_overrides(ConfigOverrides { | ||
port: Some(9000), | ||
let config = Config::load(&absolute_path)?.with_overrides(ConfigOverrides { | ||
output_dir: Some(abs_output.clone()), | ||
..Default::default() | ||
}); | ||
|
||
// Check only port is overridden | ||
assert_eq!(config.server.port, 9000); | ||
assert!(!config.build.verbose); // Unchanged | ||
assert!(config.server.hot_reload); // Default value | ||
assert_eq!(config.title, "Base Blog"); // Unchanged | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_output_dir_handling() -> Result<(), Error> { | ||
let temp_dir = TempDir::new()?; | ||
assert_eq!(config.output_dir(), abs_output); | ||
|
||
// Test relative path | ||
let config = Config::load(temp_dir.path())?.with_overrides(ConfigOverrides { | ||
output_dir: Some(PathBuf::from("relative/output")), | ||
..Default::default() | ||
}); | ||
// Test path resolution method | ||
let relative_path = PathBuf::from("relative/path"); | ||
assert_eq!( | ||
config.output_dir(), | ||
temp_dir.path().join("relative/output") // Joined to site_dir, not current_dir | ||
config.get_absolute_path(&relative_path), | ||
absolute_path.join("relative/path") | ||
); | ||
|
||
// Test absolute path | ||
let abs_path = if cfg!(windows) { | ||
PathBuf::from(r"C:\absolute\output") | ||
PathBuf::from(r"C:\absolute\path") | ||
} else { | ||
PathBuf::from("/absolute/output") | ||
PathBuf::from("/absolute/path") | ||
}; | ||
assert_eq!(config.get_absolute_path(&abs_path), abs_path); | ||
|
||
let config = Config::load(temp_dir.path())?.with_overrides(ConfigOverrides { | ||
output_dir: Some(abs_path.clone()), | ||
..Default::default() | ||
}); | ||
assert_eq!(config.output_dir(), abs_path); | ||
Ok(()) | ||
} | ||
#[test] | ||
fn test_path_helper_methods() -> Result<(), Error> { | ||
let temp_dir = TempDir::new()?; | ||
let config = Config::load(temp_dir.path())?; | ||
|
||
assert_eq!(config.posts_dir(), temp_dir.path().join("posts")); | ||
assert_eq!(config.templates_dir(), temp_dir.path().join("templates")); | ||
assert_eq!(config.static_dir(), temp_dir.path().join("static")); | ||
assert_eq!(config.output_dir(), temp_dir.path().join("dist")); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_invalid_toml() { | ||
let config_content = r#" | ||
title = "Unclosed String | ||
invalid toml content | ||
"#; | ||
|
||
let (temp_dir, _) = create_test_toml(config_content).unwrap(); | ||
let result = Config::load(temp_dir.path()); | ||
assert!(matches!(result, Err(Error::ConfigParse(_)))); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_from_str() -> Result<(), Error> { | ||
let config_str = r#" | ||
title = "From String" | ||
base_url = "http://example.com" | ||
[server] | ||
port = 3000 | ||
"#; | ||
|
||
let config: Config = toml::from_str(config_str)?; | ||
assert_eq!(config.title, "From String"); | ||
assert_eq!(config.base_url, "http://example.com"); | ||
assert_eq!(config.server.port, 3000); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_config_serialize() -> Result<(), Error> { | ||
let original_config = Config::default().with_overrides(ConfigOverrides { | ||
port: Some(9000), | ||
author: Some("Test Author".into()), | ||
..Default::default() | ||
}); | ||
|
||
// Serialize to TOML | ||
let serialized = toml::to_string(&original_config)?; | ||
|
||
// Deserialize back | ||
let deserialized: Config = toml::from_str(&serialized)?; | ||
|
||
assert_eq!(deserialized.server.port, 9000); | ||
assert_eq!(deserialized.author.name, "Test Author"); | ||
assert_eq!(deserialized.build.output_dir, "dist"); | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.