use crate::resolve::fuzzy_path::relative_asset_path;
use crate::slug::{generate_slug, normalize_separators};
use std::collections::HashMap;
pub fn resolve_path_with_overrides(path: &str, overrides: &HashMap<String, String>) -> String {
let normalized = normalize_separators(path);
let segments: Vec<&str> = normalized.split('/').collect();
let last_idx = segments.len().saturating_sub(1);
let mut resolved: Vec<String> = Vec::new();
let mut cumulative = String::new();
for (i, seg) in segments.iter().enumerate() {
if i > 0 {
cumulative.push('/');
}
cumulative.push_str(seg);
if let Some(override_slug) = overrides.get(&cumulative) {
resolved.push(override_slug.clone());
} else if i == last_idx {
resolved.push((*seg).to_string());
} else {
resolved.push(generate_slug(seg));
}
}
resolved.join("/")
}
pub fn pinned_url(root_rel: &str, overrides: &HashMap<String, String>) -> String {
let stripped = root_rel.strip_prefix('/').unwrap_or(root_rel);
let mapped = resolve_path_with_overrides(stripped, overrides);
format!(
"/{}",
crate::resolve::fuzzy_path::percent_encode_path_segments(&mapped)
)
}
pub fn reference_output_url(
from_source: &str,
target_source: &str,
overrides: &HashMap<String, String>,
) -> String {
let from_out = resolve_path_with_overrides(from_source, overrides);
let target_out = resolve_path_with_overrides(target_source, overrides);
relative_asset_path(&from_out, &target_out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slugifies_intermediate_preserves_leaf() {
let o = HashMap::new();
assert_eq!(
resolve_path_with_overrides("Resources/cities-heat-map-app/index.html", &o),
"resources/cities-heat-map-app/index.html"
);
assert_eq!(resolve_path_with_overrides("My App/index.html", &o), "my-app/index.html");
}
#[test]
fn resolve_path_handles_backslash_separators() {
let o = HashMap::new();
assert_eq!(
resolve_path_with_overrides("Sub Dir\\Winter-Song.mov", &o),
"sub-dir/Winter-Song.mov"
);
assert_eq!(
resolve_path_with_overrides("Sub Dir\\Winter-Song.mov", &o),
resolve_path_with_overrides("Sub Dir/Winter-Song.mov", &o),
);
assert!(!resolve_path_with_overrides("A\\B\\index.html", &o).contains('\\'));
}
#[test]
fn pinned_url_is_depth_independent_and_case_canonical() {
let o = HashMap::new();
assert_eq!(
pinned_url("MIRROR/在場/cover-IMG.png", &o),
"/mirror/%E5%9C%A8%E5%A0%B4/cover-IMG.png"
);
assert_eq!(
pinned_url("/MIRROR/cover-IMG.png", &o),
pinned_url("MIRROR/cover-IMG.png", &o)
);
let mut with_override = HashMap::new();
with_override.insert("图片".to_string(), "images".to_string());
assert_eq!(
pinned_url("图片/photo.jpg", &with_override),
"/images/photo.jpg"
);
assert_eq!(
pinned_url("My Photos/a b.jpg", &o),
"/my-photos/a%20b.jpg"
);
}
#[test]
fn output_url_cancels_shared_mixed_case_prefix() {
let o = HashMap::new();
assert_eq!(
reference_output_url("Resources/index.md", "Resources/app/index.html", &o),
"app/index.html"
);
assert_eq!(
reference_output_url("Research.md", "Resources/app/index.html", &o),
"resources/app/index.html"
);
}
}