diff options
author | Lars Wirzenius <liw@liw.fi> | 2022-10-22 07:05:49 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2022-10-22 07:05:49 +0000 |
commit | 91715568becfcc03f93ff1198bb06e39b40aaa3f (patch) | |
tree | 7b7571ff07aa2db2b4d22282bf2033ee66edad69 | |
parent | f9df90b851ca273e4af44cc726d5814131e0d6a8 (diff) | |
parent | a448f7fd48357cdae1bc2fc72862335dd8bb4458 (diff) | |
download | riki-91715568becfcc03f93ff1198bb06e39b40aaa3f.tar.gz |
Merge branch 'pagespec-func' into 'main'
feat: support page(glob) function in pagespec
See merge request larswirzenius/riki!58
-rw-r--r-- | src/bin/riki.rs | 4 | ||||
-rw-r--r-- | src/pagespec.lalrpop | 7 | ||||
-rw-r--r-- | src/pagespec.rs | 63 | ||||
-rw-r--r-- | src/site.rs | 33 |
4 files changed, 85 insertions, 22 deletions
diff --git a/src/bin/riki.rs b/src/bin/riki.rs index 6feae6c..d3203a4 100644 --- a/src/bin/riki.rs +++ b/src/bin/riki.rs @@ -271,7 +271,7 @@ impl PageSpec { for page in site.markdown_pages() { let path = page.meta().path(); debug!("consider markdown page {}", path.display()); - if pagespec.matches(path) { + if pagespec.matches(&site, path) { println!("{}", path.display()); } } @@ -279,7 +279,7 @@ impl PageSpec { for name in site.files_only() { let path = name.page_path(); debug!("consider file {}", path.display()); - if pagespec.matches(path) { + if pagespec.matches(&site, path) { println!("{}", path.display()); } } diff --git a/src/pagespec.lalrpop b/src/pagespec.lalrpop index c7c762a..e1dd433 100644 --- a/src/pagespec.lalrpop +++ b/src/pagespec.lalrpop @@ -8,11 +8,16 @@ pub Expr: Box<Expr> = { } Term: Box<Expr> = { - r"(\w|[._*?/-])+" => Box::new(Expr::Glob(<>.to_string())), + Glob => Box::new(Expr::Glob(<>)), + "page" "(" <g:Glob> ")" => Box::new(Expr::PageFunc(<>)), "!" <t:Term> => Box::new(Expr::Negate(t)), "(" <e:Expr> ")" => e, } +Glob: String = { + r"(\w|[._*?/-])+" => <>.to_string(), +} + Op: OpCode = { "and" => OpCode::And, "or" => OpCode::Or, diff --git a/src/pagespec.rs b/src/pagespec.rs index 6a4b35d..1ebb1b1 100644 --- a/src/pagespec.rs +++ b/src/pagespec.rs @@ -4,6 +4,7 @@ //! expression that selects pages using an expression. See the ikiwiki //! documentation for more. +use crate::site::Site; use log::trace; use std::path::{Path, PathBuf}; @@ -31,7 +32,7 @@ impl PageSpec { } /// Match a PageSpec on a page path. - pub fn matches(&self, page_path: &Path) -> bool { + pub fn matches(&self, site: &Site, page_path: &Path) -> bool { trace!( "PageSpec::matches: container={} page_path={}", self.container.display(), @@ -40,7 +41,7 @@ impl PageSpec { assert!(page_path.is_absolute()); if let Ok(path) = page_path.strip_prefix(&self.container) { let path = format!("{}", path.display()); - self.expr.matches(&path) + self.expr.matches(site, &self.container, &path) } else { false } @@ -58,19 +59,25 @@ pub enum PageSpecError { #[derive(Debug)] pub enum Expr { Glob(String), + PageFunc(String), Negate(Box<Expr>), Op(Box<Expr>, OpCode, Box<Expr>), } impl Expr { - fn matches(&self, path: &str) -> bool { + fn matches(&self, site: &Site, container: &Path, path: &str) -> bool { trace!("Expr::matches: path={:?} self={:?}", path, self); match self { Self::Glob(glob) => glob_matches(glob, path), - Self::Negate(expr) => !expr.matches(path), + Self::PageFunc(glob) => page_matches(site, container, glob, path), // FIXME: check its page + Self::Negate(expr) => !expr.matches(site, container, path), Self::Op(left, op, right) => match op { - OpCode::And => left.matches(path) && right.matches(path), - OpCode::Or => left.matches(path) || right.matches(path), + OpCode::And => { + left.matches(site, container, path) && right.matches(site, container, path) + } + OpCode::Or => { + left.matches(site, container, path) || right.matches(site, container, path) + } }, } } @@ -86,7 +93,11 @@ pub enum OpCode { fn glob_matches(glob: &str, path: &str) -> bool { let glob: Vec<char> = glob.chars().collect(); let path: Vec<char> = path.chars().collect(); - glob_matches_helper(&glob, &path) + if glob_matches_helper(&glob, &path) { + true + } else { + false + } } fn glob_matches_helper(mut glob: &[char], mut path: &[char]) -> bool { @@ -127,6 +138,19 @@ fn glob_matches_helper(mut glob: &[char], mut path: &[char]) -> bool { glob.is_empty() && path.is_empty() } +fn page_matches(site: &Site, container: &Path, glob: &str, path: &str) -> bool { + if glob_matches(glob, path) { + let full_path = container.join(path); + if site.is_page(&full_path) { + true + } else { + false + } + } else { + false + } +} + #[cfg(test)] mod test { use super::*; @@ -182,36 +206,41 @@ mod test { #[test] fn spec_fixed_page_name_matches_itself() { + let site = Site::new("/src", "/dest"); let spec = PageSpec::new(Path::new("/"), "foo").unwrap(); - assert!(spec.matches(Path::new("/foo"))); + assert!(spec.matches(&site, Path::new("/foo"))); } #[test] fn spec_glob_matches_page() { + let site = Site::new("/src", "/dest"); let spec = PageSpec::new(Path::new("/"), "foo*").unwrap(); - assert!(spec.matches(Path::new("/foobar"))); + assert!(spec.matches(&site, Path::new("/foobar"))); } #[test] fn spec_negation() { + let site = Site::new("/src", "/dest"); let spec = PageSpec::new(Path::new("/"), "!foo*").unwrap(); - assert!(!spec.matches(Path::new("/foo"))); - assert!(spec.matches(Path::new("/bar"))); + assert!(!spec.matches(&site, Path::new("/foo"))); + assert!(spec.matches(&site, Path::new("/bar"))); } #[test] fn spec_or_matches_either() { + let site = Site::new("/src", "/dest"); let spec = PageSpec::new(Path::new("/"), "foo or bar").unwrap(); - assert!(spec.matches(Path::new("/foo"))); - assert!(spec.matches(Path::new("/bar"))); - assert!(!spec.matches(Path::new("/yo"))); + assert!(spec.matches(&site, Path::new("/foo"))); + assert!(spec.matches(&site, Path::new("/bar"))); + assert!(!spec.matches(&site, Path::new("/yo"))); } #[test] fn spec_and_matches_both() { + let site = Site::new("/src", "/dest"); let spec = PageSpec::new(Path::new("/"), "foo and !bar").unwrap(); - assert!(spec.matches(Path::new("/foo"))); - assert!(!spec.matches(Path::new("/bar"))); - assert!(!spec.matches(Path::new("/yo"))); + assert!(spec.matches(&site, Path::new("/foo"))); + assert!(!spec.matches(&site, Path::new("/bar"))); + assert!(!spec.matches(&site, Path::new("/yo"))); } } diff --git a/src/site.rs b/src/site.rs index 874f3c8..10fa923 100644 --- a/src/site.rs +++ b/src/site.rs @@ -117,6 +117,14 @@ impl Site { self.files.iter() } + pub fn is_page(&self, path: &Path) -> bool { + if self.pages_that_will_exist.get(path).is_some() { + true + } else { + false + } + } + fn all_files(&self) -> Result<Vec<Name>, SiteError> { let whatchanged = git_whatchanged(self.builder.srcdir())?; @@ -250,7 +258,7 @@ impl Site { } } -#[derive(Default)] +#[derive(Default, Debug)] struct PageSet { map: HashMap<String, PathBuf>, } @@ -302,8 +310,9 @@ impl Shortcut { #[cfg(test)] mod test { - use super::{NameBuilder, Site, SiteError, WikitextPage}; + use super::{Name, NameBuilder, Site, SiteError, WikitextPage}; use crate::page::MetaBuilder; + use crate::pagespec::PageSpec; use std::{ path::{Path, PathBuf}, time::{SystemTime, UNIX_EPOCH}, @@ -324,6 +333,10 @@ mod test { WikitextPage::new(meta, "".into()) } + fn file(path: &str) -> Name { + builder().file(Path::new(path), UNIX_EPOCH) + } + #[test] fn has_no_pages_initially() { assert_eq!(site().markdown_pages().to_vec(), vec![]); @@ -430,4 +443,20 @@ mod test { Path::new("../../foo") ); } + + #[test] + fn pagespec_page_func_matches_page() { + let mut site = Site::new("/src", "/dest"); + site.add_wikitextpage(page("/src/foobar.mdwn")).unwrap(); + let spec = PageSpec::new(Path::new("/"), "page(foo*)").unwrap(); + assert!(spec.matches(&site, Path::new("/foobar"))); + } + + #[test] + fn pagespec_page_func_doesnt_match_file() { + let mut site = Site::new("/src", "/dest"); + site.add_other_file(file("/src/foobar.jpg")); + let spec = PageSpec::new(Path::new("/"), "page(foo*)").unwrap(); + assert!(!spec.matches(&site, Path::new("/foobar.jpg"))); + } } |