From eb0a3f7a022fa6dce2a43a5427262d54b2001e71 Mon Sep 17 00:00:00 2001 From: Micha White Date: Sat, 27 Sep 2025 08:37:13 -0400 Subject: Working area --- src/lib.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/workarea.rs | 74 +++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 src/lib.rs create mode 100644 src/workarea.rs (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7f6b86f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,132 @@ +#![warn(clippy::pedantic)] +#![warn(clippy::nursery)] + +use std::collections::HashMap; +use std::fs::{File, Metadata}; +use std::path::{Path, PathBuf}; +use std::time::Instant; + +mod workarea; + +struct ContributorId(String); +struct ChannelId(String); +struct PatchId(String); +struct FileId(String); +struct SpanId(String); + +struct Remote { + url: String, + name: String, +} + +struct Contributor { + id: ContributorId, + emails: Vec, + nickname: String, + realname: Option, + public_keys: Vec, +} + +struct Channel { + id: ChannelId, + name: String, + remotes: Vec, + main_remote: Option, + patches: Vec, +} + +struct Patch { + id: PatchId, + authors: Vec, + recorder: ContributorId, + metadata: HashMap, + added_spans: Vec, + deleted_spans: Vec, + added_files: Vec, + deleted_files: Vec, +} + +struct FileInfo { + id: FileId, + inode: Option, + spans: Vec, + added_by: Vec, + renamed_by: Vec<(PatchId, PathBuf)>, + deleted_by: Vec, +} + +struct Span { + id: SpanId, + file: FileId, + after: Vec, + before: Vec, + contents: Vec, + added_by: Vec, + deleted_by: Vec, +} + +type DiffAlgorithm = fn(File, File) -> Diff; + +struct Diff(Vec); + +struct DiffSpan { + left: Vec, + right: Vec, +} + +struct Log { + entries: Vec, +} + +enum LogEntry { + CreatePatch(PatchId), +} + +type RevertAlgorithm = fn(Patch) -> Patch; + +trait StagingArea { + fn list_files() -> std::io::Result; + fn open_file(path: &Path) -> std::io::Result; + fn file_metadata(path: &Path) -> std::io::Result; + fn is_file_changed(path: &Path, since: Instant) -> std::io::Result; +} + +trait Repository { + fn archive(&self) -> Vec; + + fn remotes(&self) -> Vec; + fn main_remote(&self) -> Option; + fn set_main_remote(&mut self, remote: Option); + fn add_remote(&mut self, remote: Remote); + fn delete_remote(&mut self, remote: Remote); + fn push(&self, remote: Remote); + fn pull(&mut self, remote: Remote); + + fn all_contributors(&self) -> Vec; + fn contributor(&self, id: ContributorId) -> Contributor; + fn credit(&self, filename: &Path, byte: usize) -> Option; + + fn all_channels(&self) -> Vec; + fn active_channel(&self) -> ChannelId; + fn channel(&self, id: ChannelId) -> Channel; + fn create_channel(&mut self, channel: Channel); + fn change_channel(&mut self, id: ChannelId) -> Channel; + fn rename_channel(&mut self, id: ChannelId, name: &str) -> Channel; + fn delete_channel(&mut self, id: ChannelId); + fn add_patches_to_channel(&mut self, channel: ChannelId, patches: &[PatchId]) -> Channel; + fn add_channel_to_channel(&mut self, channel: ChannelId, plus: ChannelId) -> Channel; + + fn all_patches(&self) -> Vec; + fn active_patches(&self) -> Vec; + fn patch(&self, id: PatchId) -> Option; + fn create_patch(&mut self, patch: Patch); + fn delete_patch(&mut self, id: PatchId); + fn set_active_patches(&mut self, ids: &[PatchId]); + fn combine_patches(&mut self, patches: &[PatchId]) -> Patch; + + fn active_files(&self) -> Vec; + fn file(&self, id: FileId) -> Option; + fn write_file_from_patch(&self, id: FileId, patch: PatchId) -> Vec; + + fn span(&self, id: SpanId) -> Option; +} diff --git a/src/workarea.rs b/src/workarea.rs new file mode 100644 index 0000000..d5d2e13 --- /dev/null +++ b/src/workarea.rs @@ -0,0 +1,74 @@ +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]>, +} + +pub struct WorkFileMetadata { + name: Arc, + last_modified: Instant, +} + +fn equal_to_path(a: impl AsRef, b: impl AsRef) -> bool { + a.as_ref() == b.as_ref() +} +pub fn included_files( + root: impl AsRef, + ignored_files: Option<&IgnoreFile>, + since: Option, +) -> std::io::Result> { + 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) -> std::io::Result { + 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)) + } +} -- cgit v1.2.3