Skip to content

Commit f6303a9

Browse files
committed
update return values for Wikilink checker
1 parent 89171bf commit f6303a9

4 files changed

Lines changed: 73 additions & 40 deletions

File tree

lychee-lib/src/checker/file.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use http::StatusCode;
2-
use log::warn;
2+
use log::{trace, warn};
33
use std::borrow::Cow;
44
use std::path::{Path, PathBuf};
55

@@ -81,9 +81,12 @@ impl FileChecker {
8181
///
8282
/// Returns a `Status` indicating the result of the check.
8383
pub(crate) async fn check(&self, uri: &Uri) -> Status {
84-
//only populate the wikilink filenames if the feature is enabled
84+
// only populate the wikilink filenames if the feature is enabled
8585
if self.include_wikilinks {
86-
self.setup_wikilinks();
86+
match self.setup_wikilinks() {
87+
Ok(()) => (),
88+
Err(e) => return Status::Error(e),
89+
}
8790
}
8891
let Ok(path) = uri.url.to_file_path() else {
8992
return ErrorKind::InvalidFilePath(uri.clone()).into();
@@ -331,17 +334,20 @@ impl FileChecker {
331334
}
332335

333336
// Initializes the index of the wikilink checker
334-
fn setup_wikilinks(&self) {
335-
self.wikilink_checker.index_files();
337+
fn setup_wikilinks(&self) -> Result<(), ErrorKind> {
338+
self.wikilink_checker.setup_wikilinks_index()
336339
}
340+
337341
// Tries to resolve a link by looking up the filename in the wikilink index
338342
fn apply_wikilink_check(&self, path: &Path, uri: &Uri) -> Result<PathBuf, ErrorKind> {
339343
let mut path_buf = path.to_path_buf();
340344
for ext in &self.fallback_extensions {
341345
path_buf.set_extension(ext);
342-
match self.wikilink_checker.check(&path_buf, uri) {
343-
Err(_) => { trace!("Tried to find wikilink at {path_buf}") }
344-
Ok(resolved_path) => return Ok(resolved_path),
346+
match self.wikilink_checker.contains_path(&path_buf) {
347+
None => {
348+
trace!("Tried to find wikilink {} at {}", uri, path_buf.display());
349+
}
350+
Some(resolved_path) => return Ok(resolved_path),
345351
}
346352
}
347353

lychee-lib/src/extract/markdown.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,20 @@ pub(crate) fn extract_markdown(
8888
return None;
8989
}
9090
inside_wikilink_block = true;
91-
//Ignore gitlab toc notation: https://docs.gitlab.com/user/markdown/#table-of-contents
91+
// Ignore gitlab toc notation: https://docs.gitlab.com/user/markdown/#table-of-contents
9292
if ["_TOC_".to_string(), "TOC".to_string()].contains(&dest_url.to_string()) {
9393
return None;
9494
}
9595

96-
//Strip potholes (|) from wikilinks
96+
// Strip potholes (|) from wikilinks
9797
let mut stripped_dest_url = if has_pothole {
9898
pulldown_cmark::CowStr::Borrowed(&dest_url[0..dest_url.find('|').unwrap_or(dest_url.len())])
9999
} else {
100100
dest_url.clone()
101101
};
102102

103-
//Strip fragments (#) from wikilinks, according to the obsidian spec
104-
//fragments come before potholes
103+
// Strip fragments (#) from wikilinks, according to the obsidian spec
104+
// fragments come before potholes
105105
if stripped_dest_url.contains('#') {
106106
stripped_dest_url = pulldown_cmark::CowStr::Borrowed(&dest_url[0..dest_url.find('#').unwrap_or(dest_url.len())]);
107107
}

lychee-lib/src/types/error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ pub enum ErrorKind {
170170
#[error("Status code range error")]
171171
StatusCodeSelectorError(#[from] StatusCodeSelectorError),
172172

173+
/// Error locking a Mutex
174+
#[error("Failed to lock a Mutex")]
175+
MutexPoisoned,
176+
173177
/// Test-only error variant for formatter tests
174178
/// Available in both test and debug builds to support cross-crate testing
175179
#[cfg(any(test, debug_assertions))]
@@ -334,7 +338,10 @@ impl ErrorKind {
334338
[] => "No directory links are allowed because index_files is defined and empty".to_string(),
335339
[name] => format!("An index file ({name}) is required"),
336340
[init @ .., tail] => format!("An index file ({}, or {}) is required", init.join(", "), tail),
337-
}.into()
341+
}.into(),
342+
ErrorKind::MutexPoisoned => Some (
343+
"One or more threads failed and poisoned a Mutex".to_string()
344+
)
338345
}
339346
}
340347

@@ -470,6 +477,7 @@ impl Hash for ErrorKind {
470477
Self::BasicAuthExtractorError(e) => e.to_string().hash(state),
471478
Self::Cookies(e) => e.to_string().hash(state),
472479
Self::StatusCodeSelectorError(e) => e.to_string().hash(state),
480+
Self::MutexPoisoned => "Mutex Poisoned".to_string().hash(state),
473481
}
474482
}
475483
}

lychee-lib/src/utils/wikilink_checker.rs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
use crate::{Base, ErrorKind, Uri};
2-
use log::info;
1+
use crate::{Base, ErrorKind, Result};
2+
use log::{info, warn};
33
use std::collections::HashMap;
44
use std::ffi::OsString;
55
use std::path::Path;
66
use std::sync::Mutex;
77
use std::{path::PathBuf, sync::Arc};
88
use walkdir::WalkDir;
99

10+
/// Indexes a given directory mapping filenames to their corresponding path.
11+
///
12+
/// The `WikilinkChecker` Recursively checks all subdirectories of the given
13+
/// base directory mapping any found files to the path where they can be found.
14+
/// Symlinks are ignored to prevent it from infinite loops.
1015
#[derive(Clone, Debug, Default)]
11-
// Indexes a given directory for filenames and the corresponding path
1216
pub(crate) struct WikilinkChecker {
1317
filenames: Arc<Mutex<HashMap<OsString, PathBuf>>>,
1418
basedir: Option<Base>,
@@ -18,58 +22,73 @@ impl WikilinkChecker {
1822
pub(crate) fn new(base: Option<Base>) -> Self {
1923
Self {
2024
basedir: base,
21-
..default::Default()
25+
..Default::default()
2226
}
2327
}
2428

25-
pub(crate) fn index_files(&self) {
26-
//Skip the indexing step in case the filenames are already populated
29+
/// Populates the index of the `WikilinkChecker` unless it is already populated.
30+
///
31+
/// Recursively walks the base directory mapping each filename to an absolute filepath.
32+
/// Errors if no base directory is given or if it is recognized as remote
33+
pub(crate) fn setup_wikilinks_index(&self) -> Result<()> {
34+
// Skip the indexing step in case the filenames are already populated
2735
if !self.filenames.lock().unwrap().is_empty() {
28-
return;
36+
return Ok(());
2937
}
3038
match self.basedir {
3139
None => {
32-
info!("File indexing for Wikilinks aborted as no base directory is specified");
40+
warn!("File indexing for Wikilinks aborted as no base directory is specified");
41+
Ok(())
3342
}
34-
Some(ref basetype) => match basetype {
35-
Base::Local(localbasename) => {
36-
//Start file indexing only if the Base is valid and local
43+
Some(ref base_type) => match base_type {
44+
Base::Local(local_base_name) => {
45+
// Start file indexing only if the Base is valid and local
3746
info!(
3847
"Starting file indexing for wikilinks in {}",
39-
localbasename.display()
48+
local_base_name.display()
4049
);
4150

42-
let mut filenameslock = self.filenames.lock().unwrap();
43-
for entry in WalkDir::new::<PathBuf>(localbasename.into())
44-
//actively ignore symlinks
51+
let mut lock = self
52+
.filenames
53+
.lock()
54+
.map_err(|_| ErrorKind::MutexPoisoned)?;
55+
for entry in WalkDir::new::<PathBuf>(local_base_name.into())
56+
// actively ignore symlinks
4557
.follow_links(false)
4658
.into_iter()
4759
.filter_map(std::result::Result::ok)
4860
{
4961
if let Some(filename) = entry.path().file_name() {
50-
filenameslock
51-
.insert(filename.to_ascii_lowercase(), entry.path().to_path_buf());
62+
lock.insert(filename.to_ascii_lowercase(), entry.path().to_path_buf());
5263
}
5364
}
65+
Ok(())
5466
}
67+
5568
// A remote base is of no use for the wikilink checker, silently skip over it
56-
Base::Remote(_remotebasename) => {}
69+
Base::Remote(remote_base_name) => {
70+
warn!("Error using remote base url for checking wililinks: {remote_base_name}");
71+
Ok(())
72+
}
5773
},
5874
}
5975
}
60-
61-
pub(crate) fn check(&self, path: &Path, uri: &Uri) -> Result<PathBuf, ErrorKind> {
76+
/// Checks the index for a filename. Returning the absolute path if the name is found,
77+
/// otherwise returning None
78+
pub(crate) fn contains_path(&self, path: &Path) -> Option<PathBuf> {
6279
match path.file_name() {
63-
None => Err(ErrorKind::InvalidFilePath(uri.clone())),
80+
None => None,
6481
Some(filename) => {
65-
let filenamelock = self.filenames.lock().unwrap();
66-
if filenamelock.contains_key(&filename.to_ascii_lowercase()) {
67-
Ok(filenamelock
68-
.get(&filename.to_ascii_lowercase())
69-
.expect("Could not retrieve inserted Path for discovered Wikilink-Path"))
82+
let filename_lock = self.filenames.lock().unwrap();
83+
if filename_lock.contains_key(&filename.to_ascii_lowercase()) {
84+
Some(
85+
filename_lock.get(&filename.to_ascii_lowercase()).expect(
86+
"Could not retrieve inserted Path for discovered Wikilink-Path",
87+
),
88+
)
7089
.cloned()
7190
} else {
72-
Err(ErrorKind::InvalidFilePath(uri.clone()))
91+
None
7392
}
7493
}
7594
}

0 commit comments

Comments
 (0)