summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-08-05 06:36:14 +0300
committerLars Wirzenius <liw@liw.fi>2022-08-05 14:08:53 +0300
commita855f665873d34d7797785fef0e88bf3e497af01 (patch)
treef9d6de02b882b27e5157f6ff8c646ca252132054
parentd6eabfc585e6dff203d7ee4795d7b0e19093fbb7 (diff)
downloadriki-a855f665873d34d7797785fef0e88bf3e497af01.tar.gz
feat: data type for names for files, pages in the site
A "Name" knows its source and destination file paths, and so on. Sponsored-by: author
-rw-r--r--src/lib.rs1
-rw-r--r--src/name.rs185
2 files changed, 186 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 2caefeb..b78fda2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@
//! little slow. This care implements a subset of the functionality of
//! ikiwiki in Rust, for speed.
+pub mod name;
pub mod directive;
pub mod error;
pub mod html;
diff --git a/src/name.rs b/src/name.rs
new file mode 100644
index 0000000..44328b1
--- /dev/null
+++ b/src/name.rs
@@ -0,0 +1,185 @@
+use crate::util::{join_subpath, make_path_absolute, make_path_relative_to};
+use std::fmt;
+use std::path::{Path, PathBuf};
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Name {
+ is_wikitext: bool,
+ src: PathBuf,
+ dest: PathBuf,
+ page: PathBuf,
+ page_name: String,
+}
+
+impl Name {
+ fn new(is_wikitext: bool, src: PathBuf, dest: PathBuf, page: PathBuf) -> Self {
+ Self {
+ is_wikitext,
+ src,
+ dest,
+ page: page.clone(),
+ page_name: page.to_string_lossy().into(),
+ }
+ }
+
+ pub fn is_wikitext_page(&self) -> bool {
+ self.is_wikitext
+ }
+
+ pub fn source_path(&self) -> &Path {
+ &self.src
+ }
+
+ pub fn destination_path(&self) -> &Path {
+ &self.dest
+ }
+
+ pub fn page_path(&self) -> &Path {
+ &self.page
+ }
+
+ pub fn page_name(&self) -> &str {
+ &self.page_name
+ }
+}
+
+impl PartialEq<Name> for &Name {
+ fn eq(&self, other: &Name) -> bool {
+ self.src == other.src
+ }
+}
+
+impl fmt::Display for Name {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ write!(f, "{}", self.src.display())
+ }
+}
+
+pub struct NameBuilder {
+ srcdir: PathBuf,
+ destdir: PathBuf,
+}
+
+impl NameBuilder {
+ pub fn new(srcdir: &Path, destdir: &Path) -> Self {
+ Self {
+ srcdir: srcdir.into(),
+ destdir: destdir.into(),
+ }
+ }
+
+ fn name(&self, path: &Path, ext: Option<&str>) -> Name {
+ assert!(path.starts_with(&self.srcdir));
+ let src = path.into();
+ let relative = make_path_relative_to(&self.srcdir, path);
+ let dest = join_subpath(&self.destdir, &relative);
+ let page = make_path_absolute(&relative);
+ if let Some(ext) = ext {
+ Name::new(true, src, dest.with_extension(ext), page.with_extension(""))
+ } else {
+ Name::new(false, src, dest, page)
+ }
+ }
+
+ pub fn page(&self, path: &Path) -> Name {
+ self.name(path, Some("html"))
+ }
+
+ pub fn file(&self, path: &Path) -> Name {
+ self.name(path, None)
+ }
+}
+
+#[derive(Default, Debug)]
+pub struct Names {
+ names: Vec<Name>,
+}
+
+impl Names {
+ pub fn insert(&mut self, name: Name) {
+ self.names.push(name);
+ }
+
+ pub fn iter(&self) -> impl Iterator<Item = &Name> {
+ self.names.iter()
+ }
+
+ pub fn source_paths(&self) -> impl Iterator<Item = &PathBuf> {
+ self.names.iter().map(|name| &name.src)
+ }
+
+ pub fn pages(&self) -> impl Iterator<Item = &Name> {
+ self.names.iter().filter(|name| name.is_wikitext_page())
+ }
+
+ pub fn files(&self) -> impl Iterator<Item = &Name> {
+ self.names.iter().filter(|name| !name.is_wikitext_page())
+ }
+
+ pub fn get_source_path(&self, path: &Path) -> Option<&Name> {
+ self.names.iter().find(|name| name.src == path)
+ }
+
+ pub fn get_page_path(&self, path: &Path) -> Option<&Name> {
+ self.names.iter().find(|name| name.page == path)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::{Name, NameBuilder, Names};
+ use std::path::Path;
+
+ fn builder() -> NameBuilder {
+ NameBuilder::new(Path::new("/src"), Path::new("/dest"))
+ }
+
+ #[test]
+ fn builds_page_name() {
+ let name = builder().page(Path::new("/src/foo/bar.mdwn"));
+ assert_eq!(name.source_path(), Path::new("/src/foo/bar.mdwn"));
+ assert_eq!(name.destination_path(), Path::new("/dest/foo/bar.html"));
+ assert_eq!(name.page_path(), Path::new("/foo/bar"));
+ assert_eq!(name.page_name(), "/foo/bar");
+ }
+
+ #[test]
+ fn builds_file_name() {
+ let name = builder().file(Path::new("/src/foo/bar.jpg"));
+ assert_eq!(name.source_path(), Path::new("/src/foo/bar.jpg"));
+ assert_eq!(name.destination_path(), Path::new("/dest/foo/bar.jpg"));
+ assert_eq!(name.page_path(), Path::new("/foo/bar.jpg"));
+ assert_eq!(name.page_name(), "/foo/bar.jpg");
+ }
+
+ #[test]
+ fn names_is_empty_by_default() {
+ let names = Names::default();
+ assert!(names.names.is_empty());
+ }
+
+ #[test]
+ fn names_remembers_inserted() {
+ let mut names = Names::default();
+ let name = builder().page(Path::new("/src/foo/bar.mdwn"));
+ names.insert(name.clone());
+ assert_eq!(
+ names.get_source_path(Path::new("/src/foo/bar.mdwn")),
+ Some(&name)
+ );
+ assert_eq!(names.get_page_path(Path::new("/foo/bar")), Some(&name));
+ }
+
+ #[test]
+ fn names_remembers_inserted_pages_and_files() {
+ let mut names = Names::default();
+ let page = builder().page(Path::new("/src/foo/bar.mdwn"));
+ let file = builder().file(Path::new("/src/foo/bar.jpg"));
+ names.insert(page.clone());
+ names.insert(file.clone());
+ let pages: Vec<&Name> = names.pages().collect();
+ let files: Vec<&Name> = names.files().collect();
+ assert_eq!(pages, vec![page],);
+ assert_eq!(files, vec![file],);
+ }
+}