diff options
author | Lars Wirzenius <liw@liw.fi> | 2022-07-21 18:46:04 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2022-07-21 18:46:04 +0300 |
commit | 20a825d46a2c244ff330cf6907e062722f2c4213 (patch) | |
tree | 9675ccac4b37af889965277fe0069dd1548929a7 | |
parent | d064f8de5865d563657ff10e12a48bd9a872f43e (diff) | |
download | riki-20a825d46a2c244ff330cf6907e062722f2c4213.tar.gz |
feat: wiki links are case insensitive
If a page FOO.mdwn exists, a link `[[foo]]` points to it.
Sponsored-by: author
-rw-r--r-- | riki.md | 9 | ||||
-rw-r--r-- | src/page.rs | 6 | ||||
-rw-r--r-- | src/site.rs | 59 | ||||
-rw-r--r-- | src/util.rs | 25 |
4 files changed, 81 insertions, 18 deletions
@@ -378,16 +378,19 @@ given file site/dir/foo/child.mdwn from empty given file site/dir/foo/child/grandchild.mdwn from empty when I run riki build --plain-body site output then file output/dir/foo.html contains "href="/absolute"" -then file output/dir/foo.html contains "href="dir/sibling"" -then file output/dir/foo.html contains "href="dir/foo/child"" -then file output/dir/foo.html contains "href="dir/foo/child/grandchild"" +then file output/dir/foo.html contains "href="/dir/sibling"" +then file output/dir/foo.html contains "href="/dir/foo/child"" +then file output/dir/foo.html contains "href="/dir/foo/child/grandchild"" ~~~ +Note the uppercase link to the `child` page in the test page below. + ~~~{#foo .file .markdown} [[/absolute]] [[sibling]] [[child]] [[child/grandchild]] +[[CHILD]] ~~~ ## Wiki links to pages that don't exist diff --git a/src/page.rs b/src/page.rs index 8f7c775..189f724 100644 --- a/src/page.rs +++ b/src/page.rs @@ -1,6 +1,7 @@ use crate::error::SiteError; use crate::html::{parse, Content, Element, ElementTag, HtmlPage}; use crate::site::Site; +use crate::util::join_subpath; use crate::wikitext::{Snippet, WikitextParser}; use log::{info, trace}; use std::path::{Path, PathBuf}; @@ -22,6 +23,7 @@ impl WikitextPage { .strip_prefix(&srcdir) .unwrap_or_else(|_| panic!("get stem from {}", filename.display())) .with_extension(""); + let absolute = Path::new("/").join(&relative); let name = relative .file_name() .unwrap_or_else(|| panic!("get filename from {}", relative.display())) @@ -29,7 +31,7 @@ impl WikitextPage { .to_string(); let data = std::fs::read(filename).map_err(|e| SiteError::FileRead(filename.into(), e))?; let wikitext = String::from_utf8(data).map_err(|e| SiteError::Utf8(filename.into(), e))?; - let meta = MetaBuilder::default().name(name).path(relative).build(); + let meta = MetaBuilder::default().name(name).path(absolute).build(); Ok(Self::new(meta, wikitext)) } @@ -133,7 +135,7 @@ impl PageMeta { } pub fn destination_filename(&self, destdir: &Path) -> PathBuf { - destdir.join(&self.path).with_extension("html") + join_subpath(destdir, &self.path).with_extension("html") } pub fn name(&self) -> &str { diff --git a/src/site.rs b/src/site.rs index 9bfa2bd..afdc4d7 100644 --- a/src/site.rs +++ b/src/site.rs @@ -2,7 +2,7 @@ use crate::error::SiteError; use crate::page::{MarkdownPage, UnprocessedPage, WikitextPage}; use crate::wikitext::WikitextParser; use log::{info, trace}; -use std::collections::HashSet; +use std::collections::HashMap; use std::path::{Path, PathBuf}; use walkdir::WalkDir; @@ -14,7 +14,7 @@ pub struct Site { parser: WikitextParser, srcdir: PathBuf, destdir: PathBuf, - pages: HashSet<PathBuf>, + pages: PageSet, } impl Site { @@ -30,7 +30,7 @@ impl Site { parser: WikitextParser::default(), srcdir: srcdir.as_ref().into(), destdir: destdir.as_ref().into(), - pages: HashSet::new(), + pages: PageSet::default(), } } @@ -48,7 +48,7 @@ impl Site { pub fn add_wikitextpage(&mut self, page: WikitextPage) { info!("add wikitext page {}", page.meta().path().display()); - self.pages.insert(page.meta().path().to_path_buf()); + self.pages.insert(&page); self.wikitext_pages.push(page); } @@ -141,29 +141,52 @@ impl Site { fn resolve_helper(&self, page: &Path, target: &Path) -> Result<PathBuf, SiteError> { // Is target absolute? if target.starts_with("/") { - return Ok(target.to_path_buf()); + if let Some(path) = self.pages.get(target) { + return Ok(path.into()); + } else { + return Err(SiteError::PageMissing(page.into(), target.into())); + } } // Does a sub-page exist? let path = page.join(target); - if self.have_page(path.as_path()) { - return Ok(path); + if let Some(path) = self.pages.get(&path) { + return Ok(path.into()); } // Does a sibling exist? if let Some(parent) = page.parent() { let path = parent.join(target); - if self.have_page(path.as_path()) { - return Ok(path); + if let Some(path) = self.pages.get(path.as_path()) { + return Ok(path.into()); } } // Nothing else worked, so make the target absolute. Err(SiteError::PageMissing(page.into(), target.into())) } +} + +#[derive(Default)] +struct PageSet { + map: HashMap<String, PathBuf>, +} + +impl PageSet { + fn insert(&mut self, page: &WikitextPage) { + let path = page.meta().path(); + let key = Self::normalize(path); + self.map.insert(key, path.into()); + } + + fn get(&self, path: &Path) -> Option<&Path> { + self.map.get(&Self::normalize(path)).map(|x| x.as_ref()) + } - fn have_page(&self, path: &Path) -> bool { - self.pages.contains(path) + fn normalize(path: &Path) -> String { + path.to_str() + .expect("path is UTF8") + .to_lowercase() } } @@ -186,7 +209,8 @@ mod test { #[test] fn absolute_link_resolves_to_absolute() { - let site = Site::new(".", "."); + let mut site = Site::new(".", "."); + site.add_wikitextpage(page("/yo/yoyo")); assert_eq!( site.resolve("/foo/bar", "/yo/yoyo").unwrap(), Path::new("/yo/yoyo") @@ -217,6 +241,17 @@ mod test { } #[test] + fn link_using_other_casing_is_resolved() { + let mut site = Site::new(".", "."); + site.add_wikitextpage(page("/foo/yo")); + site.process().unwrap(); + assert_eq!( + site.resolve("/foo/bar", "YO").unwrap(), + Path::new("/foo/yo") + ); + } + + #[test] fn link_to_sublpage_resolves_to_it() { let mut site = Site::new(".", "."); site.add_wikitextpage(page("/foo/bar/yo")); diff --git a/src/util.rs b/src/util.rs index e9b82f3..ec139cd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,6 @@ use crate::error::SiteError; use log::{debug, trace}; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; pub fn canonicalize(path: &Path) -> Result<PathBuf, SiteError> { path.canonicalize() @@ -34,3 +34,26 @@ pub fn copy_file_from_source(filename: &Path, output: &Path) -> Result<(), SiteE Ok(()) } + +pub fn join_subpath<P: AsRef<Path>>(parent: P, sub: P) -> PathBuf { + let sub: PathBuf = sub.as_ref() + .components() + .filter(|c| *c != Component::RootDir) + .collect(); + parent.as_ref().join(sub) +} + +#[cfg(test)] +mod test { + use super::{join_subpath, PathBuf}; + + #[test] + fn joins_relative() { + assert_eq!(join_subpath("foo", "bar"), PathBuf::from("foo/bar")); + } + + #[test] + fn joins_absolute() { + assert_eq!(join_subpath("foo", "/bar"), PathBuf::from("foo/bar")); + } +} |