1use std::path::{Path, PathBuf};
4
5pub fn resolve_image_path(dest: &str, doc_dir: Option<&Path>) -> Option<PathBuf> {
11 let dest = dest.trim();
12 if dest.is_empty() {
13 return None;
14 }
15 let lower = dest.to_ascii_lowercase();
16 if lower.starts_with("http://")
17 || lower.starts_with("https://")
18 || lower.starts_with("data:")
19 || dest.starts_with("//")
20 {
21 return None;
22 }
23 let raw = dest.strip_prefix("file://").unwrap_or(dest);
24 let path = Path::new(raw);
25 if path.is_absolute() {
26 Some(path.to_path_buf())
27 } else {
28 doc_dir.map(|d| d.join(path))
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn resolve_rejects_remote_and_anchors_relative() {
38 let dir = Path::new("/docs");
39 assert_eq!(
40 resolve_image_path("pics/cat.png", Some(dir)),
41 Some(PathBuf::from("/docs/pics/cat.png"))
42 );
43 assert_eq!(
44 resolve_image_path("/abs/cat.png", Some(dir)),
45 Some(PathBuf::from("/abs/cat.png"))
46 );
47 assert_eq!(resolve_image_path("https://x.dev/a.png", Some(dir)), None);
48 assert_eq!(
49 resolve_image_path("data:image/png;base64,AAAA", Some(dir)),
50 None
51 );
52 assert_eq!(resolve_image_path("cat.png", None), None);
54 }
55}