summaryrefslogtreecommitdiff
path: root/src/workarea.rs
blob: d5d2e13f3beb63ad19b0486e6a80f01fc56c0676 (plain)
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Instant, SystemTime};

use walkdir::WalkDir;

pub struct IgnoreFile {
	globs: Arc<[Arc<str>]>,
}

pub struct WorkFileMetadata {
	name: Arc<Path>,
	last_modified: Instant,
}

fn equal_to_path(a: impl AsRef<Path>, b: impl AsRef<Path>) -> bool {
	a.as_ref() == b.as_ref()
}
pub fn included_files(
	root: impl AsRef<Path>,
	ignored_files: Option<&IgnoreFile>,
	since: Option<SystemTime>,
) -> std::io::Result<Vec<PathBuf>> {
	let mut files = Vec::new();

	let walker = WalkDir::new(root).into_iter().filter_entry(|entry| {
		!equal_to_path(entry.path(), ".pj")
			&& !equal_to_path(entry.path(), ".ignore")
			&& entry
				.metadata()
				.ok()
				.is_some_and(|metadata| metadata.is_file())
			&& ignored_files
				.is_none_or(|file| !file.should_ignore(entry.path().to_string_lossy().deref()))
			&& since
				.zip(entry.metadata().ok())
				.and_then(|(since, metadata)| {
					metadata.modified().ok().map(|modified| (since, modified))
				})
				.is_none_or(|(since, modified)| since < modified)
	});

	for entry in walker {
		files.push(entry?.into_path());
	}

	Ok(files)
}

impl IgnoreFile {
	fn open(root: impl AsRef<Path>) -> std::io::Result<Self> {
		let mut globs = Vec::new();
		globs.push(String::from(".pj/"));

		let ignore_file = Path::join(root.as_ref(), ".ignore");
		let ignore_file = BufReader::new(File::open(ignore_file)?);
		for line in ignore_file.lines() {
			globs.push(line?);
		}

		Ok(Self {
			globs: globs.iter().map(|s| s.deref().into()).collect(),
		})
	}

	fn should_ignore(&self, path: &str) -> bool {
		self.globs
			.iter()
			.any(|glob| fast_glob::glob_match(&**glob, path))
	}
}