diff options
-rw-r--r-- | src/collect_files.rs | 56 | ||||
-rw-r--r-- | src/generate_html.rs | 24 |
2 files changed, 53 insertions, 27 deletions
diff --git a/src/collect_files.rs b/src/collect_files.rs index 64cfc30..9aa4983 100644 --- a/src/collect_files.rs +++ b/src/collect_files.rs @@ -273,27 +273,9 @@ impl Website { if !path.starts_with('/') { path = format!("{}{path}", from.parent_url()); } + let path = make_url_safe(&collapse_path(&path)); - // Iteratively collapse ".." segments. - let mut segments: Vec<&str> = path.split('/') - .filter(|s| !s.is_empty() && *s != ".") - .collect(); - 'outer: loop { - for i in 0..(segments.len().saturating_sub(1)) { - if segments[i] == ".." { - if i == 0 { - segments.remove(0); - } else { - segments.remove(i-1); - segments.remove(i-1); - } - continue 'outer; - } - } - break; - } // Find page with this path in website. - let path = make_url_safe(&segments.join("/")); for page in &self.pages { if page.full_url == path { let root = from.root(); @@ -310,6 +292,21 @@ impl Website { return None; } + pub fn has_static(&self, from: &impl LinkFrom, path: &str) -> Option<String> { + // Attach parent if not an absolute path. + let path = match !path.starts_with('/') { + true => collapse_path(&format!("{}{path}", make_url_safe(from.parent_url()))), + false => collapse_path(path), + }; + for file in &self.static_files { + if file.full_url == path { + let root = from.root(); + return Some(format!("{root}{path}")); + } + } + return None; + } + pub fn has_image(&self, file_name: &str) -> bool { let image_path = format!("images/thumb/{file_name}"); self.static_files.iter().any(|s| s.full_url == image_path) @@ -320,3 +317,24 @@ impl Website { } } + +fn collapse_path(path: &str) -> String { + // Iteratively collapse ".." segments. + let mut segments: Vec<&str> = path.split('/') + .filter(|s| !s.is_empty() && *s != ".") + .collect(); + 'outer: loop { + for i in 0..(segments.len().saturating_sub(1)) { + if segments[i] == ".." { + if i == 0 { + segments.remove(0); + } else { + segments.remove(i-1); + segments.remove(i-1); + } + continue 'outer; + } + } + return segments.join("/"); + } +} diff --git a/src/generate_html.rs b/src/generate_html.rs index 6220015..9533410 100644 --- a/src/generate_html.rs +++ b/src/generate_html.rs @@ -289,21 +289,29 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String { html.push_str(&format!("<a href='{path}' class='{class}'>{label}</a>")) } Token::ExternalLink { label, path } => { + let mut path = path.to_owned(); let mut label = label.to_string(); - // Strip the protocol from the path when using the path as a label. - if label.is_empty() { - label = path.to_string(); + + if !path.contains("://") { + // Check that the linked static file exists. + match website.has_static(page, &path) { + Some(resolved_path) => path = resolved_path, + None => warn!("Page {:?} contains link to nonexistent static file {path:?}", page.name), + } + // Take the file name as the label if the link is unlabeled. + if label.is_empty() { + label = match path.rsplit_once('/') { + Some((_, file)) => file.to_string(), + None => path.clone(), + }; + } + } else if label.is_empty() { for protocol in ["mailto://", "http://", "https://"] { if let Some(stripped) = path.strip_prefix(protocol) { label = stripped.to_string(); } } } - // Support absolute local paths. - let path = match path.strip_prefix('/') { - Some(stripped) => format!("{}{stripped}", page.root()), - None => path.to_string(), - }; let label = sanitize_text(&label); html.push_str(&format!("<a href='{path}' class='external'>{label}</a>")); } |