Skip to main content

lychee_lib/checker/wikilink/
resolver.rs

1use crate::{Base, 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 `base` is not `Some(Base::Local(_))`.
16    pub(crate) fn new(
17        base: Option<&Base>,
18        fallback_extensions: Vec<String>,
19    ) -> Result<Self, ErrorKind> {
20        let base = match base {
21            None => Err(ErrorKind::WikilinkInvalidBase(
22                "Base must be specified for wikilink checking".into(),
23            ))?,
24            Some(base) => match base {
25                Base::Local(p) => p,
26                Base::Remote(_) => Err(ErrorKind::WikilinkInvalidBase(
27                    "Base cannot be remote".to_string(),
28                ))?,
29            },
30        };
31
32        Ok(Self {
33            checker: WikilinkIndex::new(base.clone()),
34            fallback_extensions,
35        })
36    }
37    /// Resolves a wikilink by searching the index with fallback extensions.
38    pub(crate) fn resolve(&self, path: &Path, uri: &Uri) -> Result<PathBuf, ErrorKind> {
39        for ext in &self.fallback_extensions {
40            let mut candidate = path.to_path_buf();
41            candidate.set_extension(ext);
42
43            if let Some(resolved) = self.checker.contains_path(&candidate) {
44                return Ok(resolved);
45            }
46        }
47
48        Err(ErrorKind::WikilinkNotFound(uri.clone(), path.to_path_buf()))
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::{Base, ErrorKind, Uri, checker::wikilink::resolver::WikilinkResolver};
55    use test_utils::{fixture_uri, fixtures_path};
56
57    #[test]
58    fn test_wikilink_resolves_to_filename() {
59        let resolver = WikilinkResolver::new(
60            Some(&Base::Local(fixtures_path!().join("wiki"))),
61            vec!["md".to_string()],
62        )
63        .unwrap();
64        let uri = Uri {
65            url: fixture_uri!("wiki/Usage"),
66        };
67        let path = fixtures_path!().join("Usage");
68        let expected_result = fixtures_path!().join("wiki/Usage.md");
69        assert_eq!(resolver.resolve(&path, &uri), Ok(expected_result));
70    }
71
72    #[test]
73    fn test_wikilink_not_found() {
74        let resolver = WikilinkResolver::new(
75            Some(&Base::Local(fixtures_path!().join("wiki"))),
76            vec!["md".to_string()],
77        )
78        .unwrap();
79        let uri = Uri {
80            url: fixture_uri!("wiki/404"),
81        };
82        let path = fixtures_path!().join("404");
83        assert!(matches!(
84            resolver.resolve(&path, &uri),
85            Err(ErrorKind::WikilinkNotFound(..))
86        ));
87    }
88}