1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::path::{Path, PathBuf};
pub fn route_from_path(content_dir: PathBuf, path: PathBuf) -> String {
// 1. Strip the base content directory prefix
let relative_path = match path.strip_prefix(&content_dir) {
Ok(p) => p,
Err(_) => {
// This should ideally not happen if paths are correctly managed.
// Or it means the path is outside the content directory.
// Handle this error case appropriately, e.g., panic, return an error, or log.
// For now, let's just panic and halt the build because this won't output something
// visible or usable to anyone.
panic!(
"Warning: Path {:?} is not within content directory {:?}",
path, content_dir
);
}
};
let mut route_parts: Vec<String> = relative_path
.components()
.filter_map(|c| {
// Filter out relative path components, and root/prefix components
match c {
std::path::Component::Normal(os_str) => Some(os_str.to_string_lossy().into_owned()),
_ => None,
}
})
.collect();
// 2. Handle file extension and "pretty URLs"
if let Some(last_segment) = route_parts.pop() {
let original_filename_path = Path::new(&last_segment);
if original_filename_path.file_stem().is_some() {
let stem = original_filename_path
.file_stem()
.unwrap()
.to_string_lossy();
if stem == "index" {
// If it's an index file, the URI is just its parent directory
// The parent directory is already represented by the remaining route_parts
// So, no need to add "index" to the route.
// Example: content/posts/index.md -> /posts/
} else {
// For other files, use the stem as the segment and add a trailing slash
// Example: content/posts/my-post.md -> /posts/my-post/
route_parts.push(stem.into_owned());
}
}
}
// 3. Join parts with forward slashes and ensure leading/trailing slashes
let mut route = format!("/{}", route_parts.join("/"));
// Ensure trailing slash for directories, unless it's the root '/'
if route.len() > 1 {
route.push('/');
}
// Special case for root index.md (e.g., content/index.md -> /)
// If the original relative_path was just "index.md"
if relative_path.to_string_lossy() == "index.md" {
route = "/".to_string();
}
route
}
#[cfg(test)]
mod test {
use crate::Weaver;
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_route_from_path() {
let base_path_wd = std::env::current_dir().unwrap().display().to_string();
let base_path = format!("{}/test_fixtures/config", base_path_wd);
let inst = Weaver::new(format!("{}/custom_config", base_path).into());
assert_eq!(
"/blog/post1/",
route_from_path(
inst.config.content_dir.clone().into(),
format!("{}/blog/post1.md", inst.config.content_dir).into()
)
);
}
#[test]
#[should_panic]
fn test_content_out_of_path() {
let base_path_wd = std::env::current_dir().unwrap().display().to_string();
let base_path = format!("{}/test_fixtures/config", base_path_wd);
let inst = Weaver::new(format!("{}/custom_config", base_path).into());
route_from_path(
inst.config.content_dir.clone().into(),
"madeup/blog/post1.md".into(),
);
}
}