From eb0a3f7a022fa6dce2a43a5427262d54b2001e71 Mon Sep 17 00:00:00 2001 From: Micha White Date: Sat, 27 Sep 2025 08:37:13 -0400 Subject: Working area --- .gitignore | 1 + Cargo.lock | 69 +++++++++++++++++++++++++++++ Cargo.toml | 8 ++++ rustfmt.toml | 2 + src/lib.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/workarea.rs | 74 +++++++++++++++++++++++++++++++ 6 files changed, 286 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rustfmt.toml create mode 100644 src/lib.rs create mode 100644 src/workarea.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c537729 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,69 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "fast-glob" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d26eec0ae9682c457cb0f85de67ad417b716ae852736a5d94c2ad6e92a997c9" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "pj" +version = "0.1.0" +dependencies = [ + "fast-glob", + "walkdir", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-sys" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1b4b7ce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pj" +version = "0.1.0" +edition = "2024" + +[dependencies] +walkdir = "2" +fast-glob = "1" \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3efb9c3 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = true +newline_style = "Unix" \ No newline at end of file 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