lychee_lib/checker/wikilink/
resolver.rs

1use crate::{BaseInfo, ErrorKind, Uri, checker::wikilink::index::WikilinkIndex};
2use std::path::{Path, PathBuf};
3
4#[derive(Clone, Debug)]
5pub(crate) struct WikilinkResolver {
6    checker: WikilinkIndex,
7    fallback_extensions: Vec<String>,
8}
9
10/// Tries to resolve a `WikiLink` by searching for the filename in the `WikilinkIndex`
11/// Returns the path of the found file if found, otherwise an Error
12impl WikilinkResolver {
13    /// # Errors
14    ///
15    /// Fails if the URL within `base` is not a file:// URL.
16    pub(crate) fn new(
17        base: &BaseInfo,
18        fallback_extensions: Vec<String>,
19    ) -> Result<Self, ErrorKind> {
20        let base = match base {
21            BaseInfo::None => Err(ErrorKind::WikilinkInvalidBase(
22                "Base must be specified for wikilink checking".into(),
23            ))?,
24            base => base,
25        };
26        let base = base.to_file_path().ok_or(ErrorKind::WikilinkInvalidBase(
27            "Base cannot be remote".to_string(),
28        ))?;
29
30        Ok(Self {
31            checker: WikilinkIndex::new(base),
32            fallback_extensions,
33        })
34    }
35    /// Resolves a wikilink by searching the index with fallback extensions.
36    pub(crate) fn resolve(&self, path: &Path, uri: &Uri) -> Result<PathBuf, ErrorKind> {
37        for ext in &self.fallback_extensions {
38            let mut candidate = path.to_path_buf();
39            candidate.set_extension(ext);
40
41            if let Some(resolved) = self.checker.contains_path(&candidate) {
42                return Ok(resolved);
43            }
44        }
45
46        Err(ErrorKind::WikilinkNotFound(uri.clone(), path.to_path_buf()))
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use crate::{BaseInfo, ErrorKind, Uri, checker::wikilink::resolver::WikilinkResolver};
53    use test_utils::{fixture_uri, fixtures_path};
54
55    #[test]
56    fn test_wikilink_resolves_to_filename() {
57        let resolver = WikilinkResolver::new(
58            &BaseInfo::from_path(&fixtures_path!().join("wiki")).unwrap(),
59            vec!["md".to_string()],
60        )
61        .unwrap();
62        let uri = Uri {
63            url: fixture_uri!("wiki/Usage"),
64        };
65        let path = fixtures_path!().join("Usage");
66        let expected_result = fixtures_path!().join("wiki/Usage.md");
67        assert_eq!(resolver.resolve(&path, &uri), Ok(expected_result));
68    }
69
70    #[test]
71    fn test_wikilink_not_found() {
72        let resolver = WikilinkResolver::new(
73            &BaseInfo::from_path(&fixtures_path!().join("wiki")).unwrap(),
74            vec!["md".to_string()],
75        )
76        .unwrap();
77        let uri = Uri {
78            url: fixture_uri!("wiki/404"),
79        };
80        let path = fixtures_path!().join("404");
81        assert!(matches!(
82            resolver.resolve(&path, &uri),
83            Err(ErrorKind::WikilinkNotFound(..))
84        ));
85    }
86}