summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-10-22 07:05:49 +0000
committerLars Wirzenius <liw@liw.fi>2022-10-22 07:05:49 +0000
commit91715568becfcc03f93ff1198bb06e39b40aaa3f (patch)
tree7b7571ff07aa2db2b4d22282bf2033ee66edad69
parentf9df90b851ca273e4af44cc726d5814131e0d6a8 (diff)
parenta448f7fd48357cdae1bc2fc72862335dd8bb4458 (diff)
downloadriki-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.rs4
-rw-r--r--src/pagespec.lalrpop7
-rw-r--r--src/pagespec.rs63
-rw-r--r--src/site.rs33
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")));
+ }
}