diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index f13ae2f..0fdd923 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "core" -version = "0.1.3" +version = "0.1.4" edition.workspace = true license.workspace = true diff --git a/crates/core/src/class.rs b/crates/core/src/class.rs index c26de32..7f3a71a 100644 --- a/crates/core/src/class.rs +++ b/crates/core/src/class.rs @@ -1,35 +1,49 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE}; use serde::{Deserialize, Serialize}; +use crate::{Error, Result}; + /// Data struct #[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Class { + /// Path relative to PACKAGE/java, without file extension. pub path: PathBuf, pub checksum: String, } impl Class { - pub fn is_updated(&self) -> crate::Result { - // If the path is local and that file has not been updated. - Ok(self.checksum == sha256::try_digest(self.path.clone())?) + pub fn new>(package_src_root: P, file_path: P) -> Result { + let mut file_path = file_path.as_ref().to_path_buf(); + if file_path.is_absolute() { + file_path = pathsub::sub_paths(file_path.as_path(), package_src_root.as_ref()) + .ok_or(Error::MismatchedPackage)?; + } + + Ok(Self { + path: dbg!(file_path.with_extension("")), + checksum: sha256::try_digest(package_src_root.as_ref().join(file_path))?, + }) } - pub fn update(&mut self) -> crate::Result<()> { - self.checksum = sha256::try_digest(self.path.clone())?; + pub fn is_updated>(&self, class_path: P) -> Result { + // If the path is local and that file has not been updated. + Ok(class_path + .as_ref() + .join(self.path.as_path()) + .with_extension(JAVA_EXT_CLASS) + .exists() + && self.checksum == sha256::try_digest(self.path.clone())?) + } + + pub fn update>(&mut self, package_src_root: P) -> Result<()> { + self.checksum = sha256::try_digest(dbg!( + package_src_root + .as_ref() + .join(self.path.as_path()) + .with_extension(JAVA_EXT_SOURCE), + ))?; Ok(()) } } - -// TODO: Make it clearer that this is for general files, -// not nests. -impl TryFrom for Class { - type Error = crate::Error; - - fn try_from(value: PathBuf) -> Result { - Ok(Self { - path: value.clone(), - checksum: sha256::try_digest(value)?, - }) - } -} diff --git a/crates/core/src/dependency.rs b/crates/core/src/dependency.rs index 36342a9..ce45527 100644 --- a/crates/core/src/dependency.rs +++ b/crates/core/src/dependency.rs @@ -1,6 +1,7 @@ use semver::Version; use serde::{Deserialize, Serialize}; +use crate::Result; use crate::package::PackageHandler; /// Data struct @@ -26,7 +27,7 @@ impl Dependency { return self.name.clone(); } - pub fn is_updated(&self) -> crate::Result { + pub fn is_updated(&self) -> Result { // If the path is local and that file has not been updated. Ok(self.source.as_ref().is_some_and(|path| is_url(path)) && self.checksum == sha256::try_digest(self.source.as_ref().unwrap())?) diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index da6f2d3..5a030ab 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -4,6 +4,10 @@ pub type Result = std::result::Result; #[derive(Debug, From, Display)] pub enum Error { + /// Attempted to replace a value in a hash collection, + /// but there was no prime present when one was expected. + AbsentPrimeHashingError, + #[from] Io(std::io::Error), @@ -12,6 +16,8 @@ pub enum Error { MissingFileName, + MismatchedPackage, + #[from] TomlDeserialize(toml::de::Error), diff --git a/crates/core/src/nest.rs b/crates/core/src/nest.rs index 07c5cf2..c13c8c2 100644 --- a/crates/core/src/nest.rs +++ b/crates/core/src/nest.rs @@ -29,16 +29,16 @@ impl Nest { } } - pub fn write>(&self, path: P) -> crate::Result<()> { - let mut path = path.as_ref().to_path_buf(); - if path.is_dir() { - path = path.join(F_NEST_TOML); + pub fn write>(&self, project_root: P) -> crate::Result<()> { + let mut project_root = project_root.as_ref().to_path_buf(); + if project_root.is_dir() { + project_root = project_root.join(F_NEST_TOML); } Ok(OpenOptions::new() .write(true) .create(true) - .open(path)? + .open(project_root)? .write_all(toml::to_string_pretty(&self)?.as_bytes())?) } diff --git a/crates/core/src/package.rs b/crates/core/src/package.rs index 0bc47b1..4991dc4 100644 --- a/crates/core/src/package.rs +++ b/crates/core/src/package.rs @@ -1,30 +1,35 @@ use std::hash::Hash; use std::path::{Path, PathBuf}; -use fs::expand_files; use serde::{Deserialize, Serialize}; use crate::class::Class; use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey, PreyLock}; +pub const DIR_JAVA: &str = "java/"; + /// Hashing is only based off the Prey. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PackageHandler { prey: Prey, - prey_lock: Option, + prey_lock: PreyLock, + /// Path relative to WORKSPACE/src package_root: PathBuf, target_dir: PathBuf, } impl PackageHandler { - const DIR_JAVA: &str = "java/"; - pub fn new>(src_dir: P, package_root: P, target_dir: P) -> crate::Result { let package_root = src_dir.as_ref().join(package_root.as_ref()); + let prey_lock = if let Ok(prey_lock) = PreyLock::try_from(package_root.join(F_PREY_LOCK)) { + prey_lock + } else { + PreyLock::new(package_root.clone(), package_root.join(DIR_JAVA))? + }; Ok(Self { prey: Prey::try_from(package_root.join(F_PREY_TOML))?, - prey_lock: PreyLock::try_from(package_root.join(F_PREY_LOCK)).ok(), + prey_lock, package_root, target_dir: target_dir.as_ref().to_path_buf(), }) @@ -42,32 +47,26 @@ impl PackageHandler { self.prey.version() } - pub fn get_update_targets(&mut self) -> crate::Result> { - if self.prey_lock.is_none() { - // Try to pass a reference to the class so that there's mutability of the object - // available at the workspace level instead of parsing all the way down the - // tree. How I do this, idk. My brain is friend from a few days of JS. + pub fn prey_lock(&mut self) -> &mut PreyLock { + &mut self.prey_lock + } - self.prey_lock = Some(PreyLock::from(expand_files( - self.package_root.join(Self::DIR_JAVA), - )?)); - return Ok(self.prey_lock.as_ref().unwrap().classes.iter().collect()); - } - - Ok(self - .prey_lock - .as_ref() - .unwrap() - .classes + pub fn get_outdated>(&self, class_path: P) -> Vec { + self.prey_lock + .clone() + .classes() .iter() - .filter_map(|tracked| { - if tracked.is_updated().is_ok_and(|v| v == true) { - Some(tracked) - } else { - None - } + .filter(|class| { + class + .is_updated(class_path.as_ref()) + .is_ok_and(|b| b == false) }) - .collect()) + .cloned() + .collect() + } + + pub fn write_lock(&self) -> crate::Result<()> { + self.prey_lock.write(self.package_root.as_path()) } } diff --git a/crates/core/src/prey.rs b/crates/core/src/prey.rs index 25ae61c..e4dd707 100644 --- a/crates/core/src/prey.rs +++ b/crates/core/src/prey.rs @@ -1,7 +1,9 @@ +use std::collections::HashSet; use std::fs::{File, OpenOptions}; -use std::io::Read; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; +use fs::expand_files; use serde::{Deserialize, Serialize}; use crate::class::Class; @@ -68,14 +70,54 @@ impl TryFrom for Prey { /// Data struct #[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)] pub struct PreyLock { - pub classes: Vec, + classes: HashSet, } impl PreyLock { + pub fn new>(package_root: P, package_src_root: P) -> crate::Result { + Ok(Self::from_paths( + expand_files(package_root)?, + package_src_root, + )) + } + + pub fn from_paths>(paths: Vec, package_src_root: P) -> Self { + let mut lock = Self::default(); + lock.classes = paths + .iter() + .filter_map(|f| { + let dep = Class::new(package_src_root.as_ref().to_path_buf(), f.to_owned()); + if dep.is_ok() { + Some(dep.unwrap()) + } else { + None + } + }) + .collect(); + lock + } + + pub fn write>(&self, package_root: P) -> crate::Result<()> { + let mut package_root = package_root.as_ref().to_path_buf(); + if package_root.is_dir() { + package_root = package_root.join(F_PREY_LOCK); + } + + Ok(OpenOptions::new() + .write(true) + .create(true) + .open(package_root)? + .write_all(toml::to_string_pretty(&self)?.as_bytes())?) + } + pub fn with_class(&mut self, class: Class) -> &mut Self { - self.classes.push(class); + self.classes.insert(class); self } + + pub fn classes(&mut self) -> &mut HashSet { + &mut self.classes + } } /// Load the PreyLock from Prey.lock file. @@ -97,21 +139,3 @@ impl TryFrom for PreyLock { Ok(toml::from_str(buf.as_str())?) } } - -impl From> for PreyLock { - fn from(value: Vec) -> Self { - let mut lock = Self::default(); - lock.classes = value - .iter() - .filter_map(|f| { - let dep = Class::try_from(f.to_owned()); - if dep.is_ok() { - Some(dep.unwrap()) - } else { - None - } - }) - .collect(); - lock - } -} diff --git a/crates/core/src/workspace.rs b/crates/core/src/workspace.rs index 3f6ec70..c435729 100644 --- a/crates/core/src/workspace.rs +++ b/crates/core/src/workspace.rs @@ -9,10 +9,10 @@ use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE}; use serde::{Deserialize, Serialize}; use subprocess::Exec; -use crate::Error; use crate::nest::{F_NEST_LOCK, F_NEST_TOML, Nest, NestLock}; use crate::package::PackageHandler; -use crate::prey::{F_PREY_TOML, Prey}; +use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey}; +use crate::{Error, package}; #[derive(Debug)] pub struct WorkspaceHandler { @@ -57,13 +57,15 @@ impl WorkspaceHandler { Ok(workspace_manager) } - pub fn write(&self) -> crate::Result<()> { - self.write_nest()?; - + pub fn write_locks(&self) -> crate::Result<()> { if let Option::Some(lock) = self.nest_lock.clone() { lock.write(self.project_root.join(F_NEST_LOCK))?; } + for handler in self.packages.values() { + handler.write_lock()?; + } + Ok(()) } @@ -114,25 +116,39 @@ impl WorkspaceHandler { // This is the naive build pub fn build(&mut self) -> crate::Result<&mut Self> { - let mut targets = vec![]; - - for handler in self.packages.values_mut() { - targets.append(&mut handler.get_update_targets()?); - } - let compiler = java::compiler::CompilerBuilder::new() .class_path(Self::DIR_TARGET) .destination(Self::DIR_TARGET) .build(); - for target in targets.iter() { - // Possibly come up with a source file handler for this? - if let Ok(_) = compiler.clone().compile(target.path.as_path()) { - // TODO: Prevent unnecessary recompile - //target.update()?; + // No unintentional deep copying anywhere, right? + // Probably not. We'll see. + for handler in self.packages.values_mut() { + let package_src_root = self + .project_root + .join(Self::DIR_SRC) + .join(handler.name()) + .join(package::DIR_JAVA); + let targets = handler.get_outdated(self.project_root.join(Self::DIR_TARGET)); + + for mut target in targets { + dbg!(&target); + if let Ok(true) = compiler + .clone() + .compile(package_src_root.join(target.path.as_path())) + { + target.update(package_src_root.as_path())?; + handler + .prey_lock() + .classes() + .replace(target) + .ok_or(Error::AbsentPrimeHashingError)?; + } } } + self.write_locks()?; + Ok(self) } @@ -172,12 +188,30 @@ impl WorkspaceHandler { } pub fn clean(&mut self) -> crate::Result<&mut Self> { + // Clear Nest.lock if let Err(err) = std::fs::remove_file(self.project_root.join(F_NEST_LOCK)) { if err.kind() != std::io::ErrorKind::NotFound { return Err(Error::from(err)); } } + + // Clear src/**/Prey.lock + for handler in self.packages.values() { + if let Err(err) = std::fs::remove_file( + self.project_root + .join(Self::DIR_SRC) + .join(handler.name()) + .join(F_PREY_LOCK), + ) { + if err.kind() != std::io::ErrorKind::NotFound { + return Err(Error::from(err)); + } + } + } + + // Clear target/ let _ = std::fs::remove_dir_all(Self::DIR_TARGET); + Ok(self) } @@ -256,7 +290,7 @@ impl WorkspaceHandler { .create_new(true) .open(java::F_JAVA_VERSION) { - f.write_all(format!("{}\n", java::get_javac_ver()?.major.to_string()).as_bytes())?; + f.write_all(format!("{}\n", java::get_javac_version()?.major.to_string()).as_bytes())?; } Ok(())